<!DOCTYPE html><html lang="zh-CN" data-theme="light"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"><title>Spring源码系列-第10章-SpringMVC中的HandlerAdapter源码解析 | 风祈的时光录</title><meta name="keywords" content="Spring，框架，spring源码"><meta name="author" content="youthlql"><meta name="copyright" content="youthlql"><meta name="format-detection" content="telephone=no"><meta name="theme-color" content="#ffffff"><meta name="description" content="SpringMVC中的HandlerAdapter源码">
<meta property="og:type" content="article">
<meta property="og:title" content="Spring源码系列-第10章-SpringMVC中的HandlerAdapter源码解析">
<meta property="og:url" content="https://imlql.cn/post/453f802e.html">
<meta property="og:site_name" content="风祈的时光录">
<meta property="og:description" content="SpringMVC中的HandlerAdapter源码">
<meta property="og:locale" content="zh_CN">
<meta property="og:image" content="https://npm.elemecdn.com/lql_static@latest/logo/spring.png">
<meta property="article:published_time" content="2022-10-05T04:01:02.000Z">
<meta property="article:modified_time" content="2022-10-05T11:22:40.604Z">
<meta property="article:author" content="youthlql">
<meta property="article:tag" content="Spring源码">
<meta name="twitter:card" content="summary">
<meta name="twitter:image" content="https://npm.elemecdn.com/lql_static@latest/logo/spring.png"><link rel="shortcut icon" href="https://npm.elemecdn.com/youthlql@1.0.8/blog/favicon.png"><link rel="canonical" href="https://imlql.cn/post/453f802e"><link rel="preconnect" href="//cdn.jsdelivr.net"/><link rel="preconnect" href="//hm.baidu.com"/><link rel="stylesheet" href="/css/index.css"><link rel="stylesheet" href="https://npm.elemecdn.com/@fortawesome/fontawesome-free@6/css/all.min.css" media="print" onload="this.media='all'"><link rel="stylesheet" href="https://npm.elemecdn.com/@fancyapps/ui/dist/fancybox.css" media="print" onload="this.media='all'"><script>var _hmt = _hmt || [];
(function() {
  var hm = document.createElement("script");
  hm.src = "https://hm.baidu.com/hm.js?f693ff99cc7e613b88cf5b729a14b48b";
  var s = document.getElementsByTagName("script")[0]; 
  s.parentNode.insertBefore(hm, s);
})();
</script><script>const GLOBAL_CONFIG = { 
  root: '/',
  algolia: undefined,
  localSearch: {"path":"search.xml","languages":{"hits_empty":"找不到您查询的内容：${query}"}},
  translate: {"defaultEncoding":2,"translateDelay":0,"msgToTraditionalChinese":"繁","msgToSimplifiedChinese":"簡"},
  noticeOutdate: undefined,
  highlight: {"plugin":"highlighjs","highlightCopy":true,"highlightLang":true,"highlightHeightLimit":false},
  copy: {
    success: '复制成功',
    error: '复制错误',
    noSupport: '浏览器不支持'
  },
  relativeDate: {
    homepage: false,
    post: false
  },
  runtime: '天',
  date_suffix: {
    just: '刚刚',
    min: '分钟前',
    hour: '小时前',
    day: '天前',
    month: '个月前'
  },
  copyright: undefined,
  lightbox: 'fancybox',
  Snackbar: undefined,
  source: {
    justifiedGallery: {
      js: 'https://cdn.jsdelivr.net/npm/flickr-justified-gallery@2/dist/fjGallery.min.js',
      css: 'https://cdn.jsdelivr.net/npm/flickr-justified-gallery@2/dist/fjGallery.min.css'
    }
  },
  isPhotoFigcaption: false,
  islazyload: true,
  isAnchor: false
}</script><script id="config-diff">var GLOBAL_CONFIG_SITE = {
  title: 'Spring源码系列-第10章-SpringMVC中的HandlerAdapter源码解析',
  isPost: true,
  isHome: false,
  isHighlightShrink: false,
  isToc: true,
  postUpdate: '2022-10-05 19:22:40'
}</script><noscript><style type="text/css">
  #nav {
    opacity: 1
  }
  .justified-gallery img {
    opacity: 1
  }

  #recent-posts time,
  #post-meta time {
    display: inline !important
  }
</style></noscript><script>(win=>{
    win.saveToLocal = {
      set: function setWithExpiry(key, value, ttl) {
        if (ttl === 0) return
        const now = new Date()
        const expiryDay = ttl * 86400000
        const item = {
          value: value,
          expiry: now.getTime() + expiryDay,
        }
        localStorage.setItem(key, JSON.stringify(item))
      },

      get: function getWithExpiry(key) {
        const itemStr = localStorage.getItem(key)

        if (!itemStr) {
          return undefined
        }
        const item = JSON.parse(itemStr)
        const now = new Date()

        if (now.getTime() > item.expiry) {
          localStorage.removeItem(key)
          return undefined
        }
        return item.value
      }
    }
  
    win.getScript = url => new Promise((resolve, reject) => {
      const script = document.createElement('script')
      script.src = url
      script.async = true
      script.onerror = reject
      script.onload = script.onreadystatechange = function() {
        const loadState = this.readyState
        if (loadState && loadState !== 'loaded' && loadState !== 'complete') return
        script.onload = script.onreadystatechange = null
        resolve()
      }
      document.head.appendChild(script)
    })
  
      win.activateDarkMode = function () {
        document.documentElement.setAttribute('data-theme', 'dark')
        if (document.querySelector('meta[name="theme-color"]') !== null) {
          document.querySelector('meta[name="theme-color"]').setAttribute('content', '#0d0d0d')
        }
      }
      win.activateLightMode = function () {
        document.documentElement.setAttribute('data-theme', 'light')
        if (document.querySelector('meta[name="theme-color"]') !== null) {
          document.querySelector('meta[name="theme-color"]').setAttribute('content', '#ffffff')
        }
      }
      const t = saveToLocal.get('theme')
    
          if (t === 'dark') activateDarkMode()
          else if (t === 'light') activateLightMode()
        
      const asideStatus = saveToLocal.get('aside-status')
      if (asideStatus !== undefined) {
        if (asideStatus === 'hide') {
          document.documentElement.classList.add('hide-aside')
        } else {
          document.documentElement.classList.remove('hide-aside')
        }
      }
    
    const detectApple = () => {
      if(/iPad|iPhone|iPod|Macintosh/.test(navigator.userAgent)){
        document.documentElement.classList.add('apple')
      }
    }
    detectApple()
    })(window)</script><link rel="stylesheet" href="https://npm.elemecdn.com/lql_static@latest/butterfly_static/css/ali_icon.css"><link rel="stylesheet" href="https://npm.elemecdn.com/lql_static@latest/butterfly_static/css/2-24-mogai.css"><meta name="generator" content="Hexo 5.4.0"><link rel="alternate" href="/atom.xml" title="风祈的时光录" type="application/atom+xml">
</head><body><div id="web_bg"></div><div id="sidebar"><div id="menu-mask"></div><div id="sidebar-menus"><div class="avatar-img is-center"><img src= "" data-lazy-src="https://npm.elemecdn.com/lql_static@latest/avatar/2.jpg" onerror="onerror=null;src='/img/friend_404.gif'" alt="avatar"/></div><div class="site-data is-center"><div class="data-item"><a href="/archives/"><div class="headline">文章</div><div class="length-num">37</div></a></div><div class="data-item"><a href="/tags/"><div class="headline">标签</div><div class="length-num">21</div></a></div><div class="data-item"><a href="/categories/"><div class="headline">分类</div><div class="length-num">13</div></a></div></div><hr/><div class="menus_items"><div class="menus_item"><a class="site-page" href="/"><i class="fa-fw iconfont icon-shouyex"></i><span> 首页</span></a></div><div class="menus_item"><a class="site-page group" href="javascript:void(0);"><i class="fa-fw iconfont icon-zuixinwenzhang_huaban"></i><span> 找文章</span><i class="fas fa-chevron-down"></i></a><ul class="menus_item_child"><li><a class="site-page child" href="/categories/"><i class="fa-fw iconfont icon-fenlei1"></i><span> 分类</span></a></li><li><a class="site-page child" href="/tags/"><i class="fa-fw iconfont icon-biaoqian1"></i><span> 标签</span></a></li><li><a class="site-page child" href="/archives/"><i class="fa-fw iconfont icon-shijianzhou"></i><span> 归档</span></a></li></ul></div></div></div></div><div class="post" id="body-wrap"><header class="post-bg" id="page-header" style="background-image: url('https://npm.elemecdn.com/lql_static@latest/logo/spring.png')"><nav id="nav"><span id="blog_name"><a id="site-name" href="/">风祈的时光录</a></span><div id="menus"><div id="search-button"><a class="site-page social-icon search"><i class="fas fa-search fa-fw"></i><span> 搜索</span></a></div><div class="menus_items"><div class="menus_item"><a class="site-page" href="/"><i class="fa-fw iconfont icon-shouyex"></i><span> 首页</span></a></div><div class="menus_item"><a class="site-page group" href="javascript:void(0);"><i class="fa-fw iconfont icon-zuixinwenzhang_huaban"></i><span> 找文章</span><i class="fas fa-chevron-down"></i></a><ul class="menus_item_child"><li><a class="site-page child" href="/categories/"><i class="fa-fw iconfont icon-fenlei1"></i><span> 分类</span></a></li><li><a class="site-page child" href="/tags/"><i class="fa-fw iconfont icon-biaoqian1"></i><span> 标签</span></a></li><li><a class="site-page child" href="/archives/"><i class="fa-fw iconfont icon-shijianzhou"></i><span> 归档</span></a></li></ul></div></div><div id="toggle-menu"><a class="site-page"><i class="fas fa-bars fa-fw"></i></a></div></div></nav><div id="post-info"><h1 class="post-title">Spring源码系列-第10章-SpringMVC中的HandlerAdapter源码解析</h1><div id="post-meta"><div class="meta-firstline"><span class="post-meta-date"><i class="far fa-calendar-alt fa-fw post-meta-icon"></i><span class="post-meta-label">发表于</span><time class="post-meta-date-created" datetime="2022-10-05T04:01:02.000Z" title="发表于 2022-10-05 12:01:02">2022-10-05</time><span class="post-meta-separator">|</span><i class="fas fa-history fa-fw post-meta-icon"></i><span class="post-meta-label">更新于</span><time class="post-meta-date-updated" datetime="2022-10-05T11:22:40.604Z" title="更新于 2022-10-05 19:22:40">2022-10-05</time></span><span class="post-meta-categories"><span class="post-meta-separator">|</span><i class="fas fa-inbox fa-fw post-meta-icon"></i><a class="post-meta-categories" href="/categories/Spring/">Spring</a><i class="fas fa-angle-right post-meta-separator"></i><i class="fas fa-inbox fa-fw post-meta-icon"></i><a class="post-meta-categories" href="/categories/Spring/%E6%BA%90%E7%A0%81V1/">源码V1</a></span></div><div class="meta-secondline"><span class="post-meta-separator">|</span><span class="post-meta-wordcount"><i class="far fa-file-word fa-fw post-meta-icon"></i><span class="post-meta-label">字数总计:</span><span class="word-count">6k</span><span class="post-meta-separator">|</span><i class="far fa-clock fa-fw post-meta-icon"></i><span class="post-meta-label">阅读时长:</span><span>27分钟</span></span></div></div></div></header><main class="layout" id="content-inner"><div id="post"><article class="post-content" id="article-container"><h1 id="第10章-SpringMVC中的HandlerAdapter源码解析"><a href="#第10章-SpringMVC中的HandlerAdapter源码解析" class="headerlink" title="第10章-SpringMVC中的HandlerAdapter源码解析"></a>第10章-SpringMVC中的HandlerAdapter源码解析</h1><h2 id="测试类"><a href="#测试类" class="headerlink" title="测试类"></a>测试类</h2><h3 id="HelloController"><a href="#HelloController" class="headerlink" title="HelloController"></a>HelloController</h3><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">package</span> cn.imlql.web.controller;</span><br><span class="line"></span><br><span class="line"><span class="meta">@Controller</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">HelloController</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">	<span class="function"><span class="keyword">public</span> <span class="title">HelloController</span><span class="params">()</span> </span>&#123;</span><br><span class="line">		System.out.println(<span class="string">&quot;HelloController.....&quot;</span>);</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	<span class="meta">@Autowired</span></span><br><span class="line">	HelloService helloService;</span><br><span class="line"></span><br><span class="line">	<span class="meta">@GetMapping(&quot;/hello&quot;)</span></span><br><span class="line">	<span class="function"><span class="keyword">public</span> String <span class="title">sayHello</span><span class="params">(String name,</span></span></span><br><span class="line"><span class="function"><span class="params">						   <span class="meta">@RequestParam(&quot;user&quot;)</span> String user,</span></span></span><br><span class="line"><span class="function"><span class="params">						   HttpSession session)</span> </span>&#123;</span><br><span class="line">		String mvc = helloService.say(user + <span class="string">&quot;：MVC&quot;</span> + name);</span><br><span class="line">		session.setAttribute(<span class="string">&quot;msg&quot;</span>, mvc);</span><br><span class="line">		<span class="keyword">return</span> <span class="string">&quot;index.jsp&quot;</span>;</span><br><span class="line">	&#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure>

<h3 id="index-jsp"><a href="#index-jsp" class="headerlink" title="index.jsp"></a>index.jsp</h3><figure class="highlight"><table><tr><td class="code"><pre><span class="line">&lt;%@ page contentType=<span class="string">&quot;text/html;charset=UTF-8&quot;</span> language=<span class="string">&quot;java&quot;</span> %&gt;</span><br><span class="line">&lt;html&gt;</span><br><span class="line">  &lt;head&gt;</span><br><span class="line">    &lt;title&gt;$Title$&lt;/title&gt;</span><br><span class="line">  &lt;/head&gt;</span><br><span class="line">  &lt;body&gt;</span><br><span class="line">  $END$</span><br><span class="line">  结果： &lt;h1 style=&quot;color: red&quot;&gt;$&#123;sessionScope.msg&#125;&lt;/h1&gt;</span><br><span class="line">  &lt;/body&gt;</span><br><span class="line">&lt;/html&gt;</span><br></pre></td></tr></table></figure>

<p>其余的类和前面一样，不列举了。</p>
<h2 id="BeanNameUrlHandlerMapping简介"><a href="#BeanNameUrlHandlerMapping简介" class="headerlink" title="BeanNameUrlHandlerMapping简介"></a>BeanNameUrlHandlerMapping简介</h2><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">BeanNameUrlHandlerMapping</span> <span class="keyword">extends</span> <span class="title">AbstractDetectingUrlHandlerMapping</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">   <span class="comment">/**</span></span><br><span class="line"><span class="comment">    * Checks name and aliases of the given bean for URLs, starting with &quot;/&quot;.</span></span><br><span class="line"><span class="comment">    */</span></span><br><span class="line">   <span class="meta">@Override</span>  <span class="comment">//决定使用哪个url注册</span></span><br><span class="line">   <span class="keyword">protected</span> String[] determineUrlsForHandler(String beanName) &#123;</span><br><span class="line">      List&lt;String&gt; urls = <span class="keyword">new</span> ArrayList&lt;&gt;();</span><br><span class="line">      <span class="keyword">if</span> (beanName.startsWith(<span class="string">&quot;/&quot;</span>)) &#123;</span><br><span class="line">         urls.add(beanName);</span><br><span class="line">      &#125;</span><br><span class="line">      String[] aliases = obtainApplicationContext().getAliases(beanName);</span><br><span class="line">      <span class="keyword">for</span> (String alias : aliases) &#123;</span><br><span class="line">         <span class="keyword">if</span> (alias.startsWith(<span class="string">&quot;/&quot;</span>)) &#123;</span><br><span class="line">            urls.add(alias);</span><br><span class="line">         &#125;</span><br><span class="line">      &#125;</span><br><span class="line">      <span class="keyword">return</span> StringUtils.toStringArray(urls);</span><br><span class="line">   &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<ol>
<li>很简单，意思就是只要你的BeanName是以/开头，就会将这个Bean封装成一个BeanNameUrlHandlerMapping映射。</li>
<li>具体路径怎么写呢？你可以在你的类上写一个<code>@Controller(&quot;/helloReq&quot;)</code> ，这样就有路径了。代表的意思就是<code>/helloReq</code>这个URL和由哪个handler来处理的映射关系被保存在了BeanNameUrlHandlerMapping里</li>
<li>如果不懂的话可以待会看下面的例子</li>
</ol>
<blockquote>
<p>handler就是咱们常说的XXXController</p>
</blockquote>
<h2 id="HandlerAdapter概述"><a href="#HandlerAdapter概述" class="headerlink" title="HandlerAdapter概述"></a>HandlerAdapter概述</h2><ol>
<li>上面我们从HandlerMapping这个映射中心找到了由哪个Controller执行哪个方法</li>
<li>按照我们最简单的想法就是直接method.invoke()反射执行</li>
<li>但是实际上我们要考虑<ol>
<li>是哪个对象执行方法？</li>
<li>方法里的参数有几个？</li>
<li>参数类型是什么？参数类型大概率有我们自己写的类，怎么处理</li>
<li>多个参数我们还要一个一个赋值。</li>
<li>怎么返回？是直接返回值，还是跳转页面，等等</li>
</ol>
</li>
<li>其实是比较复杂的，springmvc就写了一个HandlerAdapter来处理这些复杂的逻辑</li>
</ol>
<p>还是跟HandlerMapping一样，继续Debug  <code>DispatcherServlet#doDispatch(HttpServletRequest, HttpServletResponse)</code></p>
<p>浏览器输入：<a target="_blank" rel="noopener" href="http://localhost:8080/springmvc_source_test/hello?name=zhangsan&amp;user=haha%E5%BC%80%E5%A7%8B%E6%B5%8B%E8%AF%95">http://localhost:8080/springmvc_source_test/hello?name=zhangsan&amp;user=haha开始测试</a></p>
<h3 id="DispatcherServlet-doDispatch-请求派发"><a href="#DispatcherServlet-doDispatch-请求派发" class="headerlink" title="DispatcherServlet#doDispatch()请求派发"></a>DispatcherServlet#doDispatch()请求派发</h3><img src= "" data-lazy-src="https://upyunimg.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_10/image-20211019110419677.png"  />



<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="comment">//SpringMVC处理请求的核心流程</span></span><br><span class="line"><span class="function"><span class="keyword">protected</span> <span class="keyword">void</span> <span class="title">doDispatch</span><span class="params">(HttpServletRequest request, HttpServletResponse response)</span> <span class="keyword">throws</span> Exception </span>&#123;</span><br><span class="line">   HttpServletRequest processedRequest = request;</span><br><span class="line">   HandlerExecutionChain mappedHandler = <span class="keyword">null</span>; <span class="comment">//handler（目标方法）的执行链</span></span><br><span class="line">   <span class="keyword">boolean</span> multipartRequestParsed = <span class="keyword">false</span>; <span class="comment">//文件上传标志</span></span><br><span class="line">   <span class="comment">//对异步请求的支持（Servlet3.0以后才有的，Webflux）</span></span><br><span class="line">   WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);</span><br><span class="line"></span><br><span class="line">   <span class="keyword">try</span> &#123;</span><br><span class="line">      ModelAndView mv = <span class="keyword">null</span>;</span><br><span class="line">      Exception dispatchException = <span class="keyword">null</span>;</span><br><span class="line"></span><br><span class="line">      <span class="keyword">try</span> &#123;</span><br><span class="line">         processedRequest = checkMultipart(request); <span class="comment">//检查当前是否文件上传请求</span></span><br><span class="line">         multipartRequestParsed = (processedRequest != request);</span><br><span class="line"></span><br><span class="line">         <span class="comment">//构造出了【目标方法+拦截器整个链路】决定使用哪个Handler处理当前请求 Determine handler for the current request.</span></span><br><span class="line">         mappedHandler = getHandler(processedRequest);</span><br><span class="line">         <span class="keyword">if</span> (mappedHandler == <span class="keyword">null</span>) &#123;  <span class="comment">//如果找不到人处理，就send 404</span></span><br><span class="line">            noHandlerFound(processedRequest, response);</span><br><span class="line">            <span class="keyword">return</span>;</span><br><span class="line">         &#125;</span><br><span class="line"></span><br><span class="line">         <span class="comment">//适配器怎么找的、 Determine handler adapter for the current request.</span></span><br><span class="line">         HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());</span><br><span class="line"></span><br><span class="line">         <span class="comment">// Process last-modified header, if supported by the handler.</span></span><br><span class="line">         String method = request.getMethod();</span><br><span class="line">         <span class="keyword">boolean</span> isGet = <span class="string">&quot;GET&quot;</span>.equals(method);</span><br><span class="line">         <span class="keyword">if</span> (isGet || <span class="string">&quot;HEAD&quot;</span>.equals(method)) &#123;</span><br><span class="line">            <span class="keyword">long</span> lastModified = ha.getLastModified(request, mappedHandler.getHandler());</span><br><span class="line">            <span class="keyword">if</span> (<span class="keyword">new</span> ServletWebRequest(request, response).checkNotModified(lastModified) &amp;&amp; isGet) &#123;</span><br><span class="line">               <span class="keyword">return</span>;</span><br><span class="line">            &#125;</span><br><span class="line">         &#125;</span><br><span class="line">         <span class="comment">//所有拦截器的 preHandle 执行</span></span><br><span class="line">         <span class="keyword">if</span> (!mappedHandler.applyPreHandle(processedRequest, response)) &#123;</span><br><span class="line">            <span class="keyword">return</span>; <span class="comment">//使用 mappedHandler整个链</span></span><br><span class="line">         &#125;</span><br><span class="line"></span><br><span class="line">         <span class="comment">//真正来执行目标方法 Actually invoke the handler.</span></span><br><span class="line">         mv = ha.handle(processedRequest, response, mappedHandler.getHandler());</span><br><span class="line"></span><br><span class="line">         <span class="keyword">if</span> (asyncManager.isConcurrentHandlingStarted()) &#123;</span><br><span class="line">            <span class="keyword">return</span>;</span><br><span class="line">         &#125;</span><br><span class="line"></span><br><span class="line">         applyDefaultViewName(processedRequest, mv);</span><br><span class="line">         mappedHandler.applyPostHandle(processedRequest, response, mv);</span><br><span class="line">      &#125;</span><br><span class="line">      <span class="keyword">catch</span> (Exception ex) &#123;</span><br><span class="line">         dispatchException = ex;</span><br><span class="line">      &#125;</span><br><span class="line">      <span class="keyword">catch</span> (Throwable err) &#123;</span><br><span class="line">         <span class="comment">// As of 4.3, we&#x27;re processing Errors thrown from handler methods as well,</span></span><br><span class="line">         <span class="comment">// making them available for @ExceptionHandler methods and other scenarios.</span></span><br><span class="line">         dispatchException = <span class="keyword">new</span> NestedServletException(<span class="string">&quot;Handler dispatch failed&quot;</span>, err);</span><br><span class="line">      &#125; <span class="comment">//处理结果</span></span><br><span class="line">      processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);</span><br><span class="line">   &#125;</span><br><span class="line">   <span class="keyword">catch</span> (Exception ex) &#123;</span><br><span class="line">      triggerAfterCompletion(processedRequest, response, mappedHandler, ex);</span><br><span class="line">   &#125;</span><br><span class="line">   <span class="keyword">catch</span> (Throwable err) &#123;</span><br><span class="line">      triggerAfterCompletion(processedRequest, response, mappedHandler,</span><br><span class="line">            <span class="keyword">new</span> NestedServletException(<span class="string">&quot;Handler processing failed&quot;</span>, err));</span><br><span class="line">   &#125;</span><br><span class="line">   <span class="keyword">finally</span> &#123;</span><br><span class="line">      <span class="keyword">if</span> (asyncManager.isConcurrentHandlingStarted()) &#123;</span><br><span class="line">         <span class="comment">// Instead of postHandle and afterCompletion</span></span><br><span class="line">         <span class="keyword">if</span> (mappedHandler != <span class="keyword">null</span>) &#123;</span><br><span class="line">            mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);</span><br><span class="line">         &#125;</span><br><span class="line">      &#125;</span><br><span class="line">      <span class="keyword">else</span> &#123;</span><br><span class="line">         <span class="comment">// Clean up any resources used by a multipart request.</span></span><br><span class="line">         <span class="keyword">if</span> (multipartRequestParsed) &#123;</span><br><span class="line">            cleanupMultipart(processedRequest);</span><br><span class="line">         &#125;</span><br><span class="line">      &#125;</span><br><span class="line">   &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<p>F7进入<code>getHandlerAdapter(mappedHandler.getHandler())</code></p>
<h3 id="DispatcherServlet-getHandlerAdapter"><a href="#DispatcherServlet-getHandlerAdapter" class="headerlink" title="DispatcherServlet#getHandlerAdapter()"></a>DispatcherServlet#getHandlerAdapter()</h3><img src= "" data-lazy-src="https://upyunimg.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_10/image-20211019111247967.png"  />

<p>上面几个默认的适配器还是在<code>DispatcherServlet.properties</code>配置的4个默认适配器，然后在初始化九大组件的时候放到容器里</p>
<h3 id="HttpRequestHandlerAdapter-supports"><a href="#HttpRequestHandlerAdapter-supports" class="headerlink" title="HttpRequestHandlerAdapter#supports()"></a>HttpRequestHandlerAdapter#supports()</h3><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="meta">@Override</span> <span class="comment">//想让他工作，实现HttpRequestHandler接口即可</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">supports</span><span class="params">(Object handler)</span> </span>&#123; <span class="comment">//写一个HttpRequestHandler的实现也能处理请求</span></span><br><span class="line">   <span class="keyword">return</span> (handler <span class="keyword">instanceof</span> HttpRequestHandler);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>这里判断当前handler【就是咱们写的Controller】是否实现了HttpRequestHandler接口，是的话就直接返回这个Adapter，不往下走了</p>
<h3 id="SimpleControllerHandlerAdapter-supports"><a href="#SimpleControllerHandlerAdapter-supports" class="headerlink" title="SimpleControllerHandlerAdapter#supports()"></a>SimpleControllerHandlerAdapter#supports()</h3><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="meta">@Override</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">supports</span><span class="params">(Object handler)</span> </span>&#123;</span><br><span class="line">   <span class="keyword">return</span> (handler <span class="keyword">instanceof</span> Controller);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>同理，这里判断当前handler是否实现了Controller接口，是的话就直接返回这个Adapter，不往下走了</p>
<h3 id="RequestMappingHandlerAdapter"><a href="#RequestMappingHandlerAdapter" class="headerlink" title="RequestMappingHandlerAdapter"></a>RequestMappingHandlerAdapter</h3><p>RequestMappingHandlerAdapter自己没有写<code>supports</code>方法，它用的是父类的AbstractHandlerMethodAdapter的</p>
<h4 id="AbstractHandlerMethodAdapter-supports"><a href="#AbstractHandlerMethodAdapter-supports" class="headerlink" title="AbstractHandlerMethodAdapter#supports()"></a>AbstractHandlerMethodAdapter#supports()</h4><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">final</span> <span class="keyword">boolean</span> <span class="title">supports</span><span class="params">(Object handler)</span> </span>&#123;</span><br><span class="line">   <span class="keyword">return</span> (handler <span class="keyword">instanceof</span> HandlerMethod &amp;&amp; supportsInternal((HandlerMethod) handler));</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>这个就是判断当前handler是不是HandlerMethod类型的，前面大致讲过只要标注了@RequestMapping注解的handler最终都会被封装成HandlerMethod</p>
<img src= "" data-lazy-src="https://upyunimg.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_10/image-20211019194635523.png"  />

<h4 id="RequestMappingHandlerAdapter-supportsInternal"><a href="#RequestMappingHandlerAdapter-supportsInternal" class="headerlink" title="RequestMappingHandlerAdapter#supportsInternal()"></a>RequestMappingHandlerAdapter#supportsInternal()</h4><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">protected</span> <span class="keyword">boolean</span> <span class="title">supportsInternal</span><span class="params">(HandlerMethod handlerMethod)</span> </span>&#123;</span><br><span class="line">   <span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h3 id="DispatcherServlet-doDispatch"><a href="#DispatcherServlet-doDispatch" class="headerlink" title="DispatcherServlet#doDispatch()"></a>DispatcherServlet#doDispatch()</h3><img src= "" data-lazy-src="https://upyunimg.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_10/image-20211019194742173.png"/>

<h2 id="举例说明HttpRequestHandlerAdapter和SimpleControllerHandlerAdapter如何与BeanNameUrlHandlerMapping联动"><a href="#举例说明HttpRequestHandlerAdapter和SimpleControllerHandlerAdapter如何与BeanNameUrlHandlerMapping联动" class="headerlink" title="举例说明HttpRequestHandlerAdapter和SimpleControllerHandlerAdapter如何与BeanNameUrlHandlerMapping联动"></a>举例说明HttpRequestHandlerAdapter和SimpleControllerHandlerAdapter如何与BeanNameUrlHandlerMapping联动</h2><h3 id="测试类-HelloHttpRequestHandler"><a href="#测试类-HelloHttpRequestHandler" class="headerlink" title="测试类-HelloHttpRequestHandler"></a>测试类-HelloHttpRequestHandler</h3><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="meta">@Controller(&quot;/helloReq&quot;)</span> <span class="comment">//BeanNameUrlHandlerMapping 就会把他注册进去</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">HelloHttpRequestHandler</span> <span class="keyword">implements</span> <span class="title">HttpRequestHandler</span> </span>&#123;</span><br><span class="line">   <span class="comment">//启用 HttpRequestHandlerAdapter</span></span><br><span class="line"></span><br><span class="line">   <span class="comment">//处理请求</span></span><br><span class="line">   <span class="meta">@Override</span></span><br><span class="line">   <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">handleRequest</span><span class="params">(HttpServletRequest request,</span></span></span><br><span class="line"><span class="function"><span class="params">                       HttpServletResponse response)</span> <span class="keyword">throws</span> ServletException, IOException </span>&#123;</span><br><span class="line">      response.getWriter().write(<span class="string">&quot;HelloHttpRequestHandler....&quot;</span>);</span><br><span class="line">   &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p><code>@Controller(&quot;/helloReq&quot;)</code>里的/helloReq即是BeanName也是URL。这里就是之前说的BeanName以/为开头的Bean，它的URL和handler的映射关系会被保存在BeanNameUrlHandlerMapping里，如下图</p>
<img src= "" data-lazy-src="https://upyunimg.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_10/image-20211019200240789.png" />



<h3 id="DispatcherServlet-getHandlerAdapter-1"><a href="#DispatcherServlet-getHandlerAdapter-1" class="headerlink" title="DispatcherServlet#getHandlerAdapter()"></a>DispatcherServlet#getHandlerAdapter()</h3><img src= "" data-lazy-src="https://upyunimg.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_10/image-20211019195313996.png" />

<p>因为我们的HelloHttpRequestHandler正好实现了HttpRequestHandler，所以就会直接返回HttpRequestHandlerAdapter</p>
<h3 id="DispatcherServlet-doDispatch-1"><a href="#DispatcherServlet-doDispatch-1" class="headerlink" title="DispatcherServlet#doDispatch()"></a>DispatcherServlet#doDispatch()</h3><p>我们直接来到准备执行的地方</p>
<img src= "" data-lazy-src="https://upyunimg.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_10/image-20211019195555663.png" />

<p>F7进入<code>ha.handle(processedRequest, response, mappedHandler.getHandler())</code></p>
<h3 id="HttpRequestHandlerAdapter-handle"><a href="#HttpRequestHandlerAdapter-handle" class="headerlink" title="HttpRequestHandlerAdapter#handle()"></a>HttpRequestHandlerAdapter#handle()</h3><img src= "" data-lazy-src="https://upyunimg.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_10/image-20211019195838240.png"  />

<p>直接来到了这里，接着就调用我们自定义的HelloHttpRequestHandler的handleRequest方法。</p>
<h3 id="测试类-HelloSimpleController"><a href="#测试类-HelloSimpleController" class="headerlink" title="测试类-HelloSimpleController"></a>测试类-HelloSimpleController</h3><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="comment">//@RequestMapping(&quot;/ssss&quot;)</span></span><br><span class="line"><span class="meta">@Controller(&quot;/helloSimple&quot;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">HelloSimpleController</span> <span class="keyword">implements</span> <span class="title">Controller</span> </span>&#123;</span><br><span class="line">   <span class="meta">@Override</span></span><br><span class="line">   <span class="function"><span class="keyword">public</span> ModelAndView <span class="title">handleRequest</span><span class="params">(HttpServletRequest request, HttpServletResponse response)</span> <span class="keyword">throws</span> Exception </span>&#123;</span><br><span class="line">      <span class="keyword">return</span> <span class="keyword">null</span>;</span><br><span class="line">   &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>实现Controller接口的就是SimpleControllerHandlerAdapter来处理，原理和HttpRequestHandlerAdapter几乎一样。</p>
<h3 id="小总结"><a href="#小总结" class="headerlink" title="小总结"></a>小总结</h3><ol>
<li>很显然BeanNameUrlHandlerMapping和HttpRequestHandlerAdapter的结合，一个类只能处理一个URL路径的请求。并且这里的参数处理都很麻烦，不像@RequestMapping那么方便</li>
<li>远不如RequestMappingHandlerMapping和RequestMappingHandlerAdapter的结合来的方便。它两结合后，一个类里可以写多个@RequestMapping注解标注的方法，一个类里就可以处理多个URL请求，并且处理参数和返回值都很方便。</li>
<li>所以下面就重点讲RequestMappingHandlerAdapter</li>
</ol>
<h2 id="RequestMappingHandlerAdapter中的参数解析器、返回值处理器-gt-概述"><a href="#RequestMappingHandlerAdapter中的参数解析器、返回值处理器-gt-概述" class="headerlink" title="RequestMappingHandlerAdapter中的参数解析器、返回值处理器=&gt;概述"></a>RequestMappingHandlerAdapter中的参数解析器、返回值处理器=&gt;概述</h2><h3 id="DispatcherServlet-doDispatch-2"><a href="#DispatcherServlet-doDispatch-2" class="headerlink" title="DispatcherServlet#doDispatch()"></a>DispatcherServlet#doDispatch()</h3><img src= "" data-lazy-src="https://upyunimg.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_10/image-20211020111632694.png"  />







<h3 id="AbstractHandlerMethodAdapter-handle"><a href="#AbstractHandlerMethodAdapter-handle" class="headerlink" title="AbstractHandlerMethodAdapter#handle()"></a>AbstractHandlerMethodAdapter#handle()</h3><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">final</span> ModelAndView <span class="title">handle</span><span class="params">(HttpServletRequest request, HttpServletResponse response, Object handler)</span></span></span><br><span class="line"><span class="function">      <span class="keyword">throws</span> Exception </span>&#123;</span><br><span class="line"></span><br><span class="line">   <span class="keyword">return</span> handleInternal(request, response, (HandlerMethod) handler);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<p>AbstractHandlerMethodAdapter是RequestMappingHandlerAdapter的父类，之前说过</p>
<h3 id="RequestMappingHandlerAdapter-handleInternal"><a href="#RequestMappingHandlerAdapter-handleInternal" class="headerlink" title="RequestMappingHandlerAdapter#handleInternal()"></a>RequestMappingHandlerAdapter#handleInternal()</h3><figure class="highlight java"><table><tr><td class="code"><pre><span class="line">   <span class="function"><span class="keyword">protected</span> ModelAndView <span class="title">handleInternal</span><span class="params">(HttpServletRequest request,</span></span></span><br><span class="line"><span class="function"><span class="params">         HttpServletResponse response, HandlerMethod handlerMethod)</span> <span class="keyword">throws</span> Exception </span>&#123;</span><br><span class="line"></span><br><span class="line">      ModelAndView mav;</span><br><span class="line">      checkRequest(request);</span><br><span class="line"></span><br><span class="line">      <span class="comment">// 会话锁，每一个用户和服务器交互无论发了多少请求都只有一个会话，限制用户的线程 Execute invokeHandlerMethod in synchronized block if required.</span></span><br><span class="line">      <span class="keyword">if</span> (<span class="keyword">this</span>.synchronizeOnSession) &#123;</span><br><span class="line">         HttpSession session = request.getSession(<span class="keyword">false</span>);</span><br><span class="line">         <span class="keyword">if</span> (session != <span class="keyword">null</span>) &#123;</span><br><span class="line">            Object mutex = WebUtils.getSessionMutex(session);</span><br><span class="line">            <span class="keyword">synchronized</span> (mutex) &#123; <span class="comment">//高并发可以限制一个用户一次进来一个请求</span></span><br><span class="line">               mav = invokeHandlerMethod(request, response, handlerMethod);</span><br><span class="line">            &#125;</span><br><span class="line">         &#125;</span><br><span class="line">         <span class="keyword">else</span> &#123;</span><br><span class="line">            <span class="comment">// No HttpSession available -&gt; no mutex necessary</span></span><br><span class="line">            mav = invokeHandlerMethod(request, response, handlerMethod);</span><br><span class="line">         &#125;</span><br><span class="line">      &#125;</span><br><span class="line">      <span class="keyword">else</span> &#123;</span><br><span class="line">         <span class="comment">//执行目标方法 No synchronization on session demanded at all...</span></span><br><span class="line">         mav = invokeHandlerMethod(request, response, handlerMethod);</span><br><span class="line">      &#125;</span><br><span class="line"></span><br><span class="line">      <span class="keyword">if</span> (!response.containsHeader(HEADER_CACHE_CONTROL)) &#123;</span><br><span class="line">         <span class="keyword">if</span> (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) &#123;</span><br><span class="line">            applyCacheSeconds(response, <span class="keyword">this</span>.cacheSecondsForSessionAttributeHandlers);</span><br><span class="line">         &#125;</span><br><span class="line">         <span class="keyword">else</span> &#123;</span><br><span class="line">            prepareResponse(response);</span><br><span class="line">         &#125;</span><br><span class="line">      &#125;</span><br><span class="line"></span><br><span class="line">      <span class="keyword">return</span> mav;</span><br><span class="line">   &#125;</span><br><span class="line"></span><br><span class="line">   <span class="comment">//session锁这个东西不适用于高并发场景，所以spring默认是不适用它，应该是可以开启的，具体怎么开启我没研究，可以看官方文档</span></span><br><span class="line"><span class="keyword">private</span> <span class="keyword">boolean</span> synchronizeOnSession = <span class="keyword">false</span>;</span><br></pre></td></tr></table></figure>



<img src= "" data-lazy-src="https://upyunimg.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_10/image-20211020112509616.png"/>





<h3 id="RequestMappingHandlerAdapter-invokeHandlerMethod"><a href="#RequestMappingHandlerAdapter-invokeHandlerMethod" class="headerlink" title="RequestMappingHandlerAdapter#invokeHandlerMethod( )"></a>RequestMappingHandlerAdapter#invokeHandlerMethod( )</h3><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Invoke the &#123;<span class="doctag">@link</span> RequestMapping&#125; handler method preparing a &#123;<span class="doctag">@link</span> ModelAndView&#125;</span></span><br><span class="line"><span class="comment"> * if view resolution is required.</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@since</span> 4.2</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@see</span> #createInvocableHandlerMethod(HandlerMethod)</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@Nullable</span></span><br><span class="line"><span class="function"><span class="keyword">protected</span> ModelAndView <span class="title">invokeHandlerMethod</span><span class="params">(HttpServletRequest request,</span></span></span><br><span class="line"><span class="function"><span class="params">      HttpServletResponse response, HandlerMethod handlerMethod)</span> <span class="keyword">throws</span> Exception </span>&#123;</span><br><span class="line">   <span class="comment">//把原生的request，response封装到一个对象中方便后续只用这一个参数就行【装饰器模式】</span></span><br><span class="line">   ServletWebRequest webRequest = <span class="keyword">new</span> ServletWebRequest(request, response);</span><br><span class="line">   <span class="keyword">try</span> &#123; <span class="comment">//数据绑定器</span></span><br><span class="line">      WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);</span><br><span class="line">       <span class="comment">//获取到模型工厂 Model（要交给页面的数据） View（我们要去的 视图），视图可以理解为页面或者图片等等</span></span><br><span class="line">      ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);</span><br><span class="line">     </span><br><span class="line">      <span class="comment">// 这里就是做了一些增强，比如我们可以直接通过ServletInvocableHandlerMethod拿到返回值，可以很方便的拿到其它我们需要的信息</span></span><br><span class="line">      ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);</span><br><span class="line">      <span class="keyword">if</span> (<span class="keyword">this</span>.argumentResolvers != <span class="keyword">null</span>) &#123; <span class="comment">//参数解析器</span></span><br><span class="line">         invocableMethod.setHandlerMethodArgumentResolvers(<span class="keyword">this</span>.argumentResolvers);</span><br><span class="line">      &#125;</span><br><span class="line">      <span class="keyword">if</span> (<span class="keyword">this</span>.returnValueHandlers != <span class="keyword">null</span>) &#123; <span class="comment">//返回值解析器</span></span><br><span class="line">         invocableMethod.setHandlerMethodReturnValueHandlers(<span class="keyword">this</span>.returnValueHandlers);</span><br><span class="line">      &#125;</span><br><span class="line">      invocableMethod.setDataBinderFactory(binderFactory);</span><br><span class="line">      invocableMethod.setParameterNameDiscoverer(<span class="keyword">this</span>.parameterNameDiscoverer);</span><br><span class="line">      <span class="comment">//以上的 几个核心组件都挺重要的  ModelAndViewContainer是以后处理过程中产生的ModelAndView数据临时存储容器</span></span><br><span class="line">      <span class="comment">// 也是整个请求处理流程，线程共享数据</span></span><br><span class="line">      ModelAndViewContainer mavContainer = <span class="keyword">new</span> ModelAndViewContainer();</span><br><span class="line">      mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));</span><br><span class="line">      modelFactory.initModel(webRequest, mavContainer, invocableMethod);</span><br><span class="line">      mavContainer.setIgnoreDefaultModelOnRedirect(<span class="keyword">this</span>.ignoreDefaultModelOnRedirect);</span><br><span class="line">      <span class="comment">//异步请求有关的</span></span><br><span class="line">      AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response);</span><br><span class="line">      asyncWebRequest.setTimeout(<span class="keyword">this</span>.asyncRequestTimeout);</span><br><span class="line"></span><br><span class="line">      WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);</span><br><span class="line">      asyncManager.setTaskExecutor(<span class="keyword">this</span>.taskExecutor);</span><br><span class="line">      asyncManager.setAsyncWebRequest(asyncWebRequest);</span><br><span class="line">      asyncManager.registerCallableInterceptors(<span class="keyword">this</span>.callableInterceptors);</span><br><span class="line">      asyncManager.registerDeferredResultInterceptors(<span class="keyword">this</span>.deferredResultInterceptors);</span><br><span class="line"></span><br><span class="line">      <span class="keyword">if</span> (asyncManager.hasConcurrentResult()) &#123;</span><br><span class="line">         Object result = asyncManager.getConcurrentResult();</span><br><span class="line">         mavContainer = (ModelAndViewContainer) asyncManager.getConcurrentResultContext()[<span class="number">0</span>];</span><br><span class="line">         asyncManager.clearConcurrentResult();</span><br><span class="line">         LogFormatUtils.traceDebug(logger, traceOn -&gt; &#123;</span><br><span class="line">            String formatted = LogFormatUtils.formatValue(result, !traceOn);</span><br><span class="line">            <span class="keyword">return</span> <span class="string">&quot;Resume with async result [&quot;</span> + formatted + <span class="string">&quot;]&quot;</span>;</span><br><span class="line">         &#125;);</span><br><span class="line">         invocableMethod = invocableMethod.wrapConcurrentResult(result);</span><br><span class="line">      &#125;</span><br><span class="line">      <span class="comment">//真正开始执行目标方法</span></span><br><span class="line">      invocableMethod.invokeAndHandle(webRequest, mavContainer);</span><br><span class="line">      <span class="keyword">if</span> (asyncManager.isConcurrentHandlingStarted()) &#123;</span><br><span class="line">         <span class="keyword">return</span> <span class="keyword">null</span>;</span><br><span class="line">      &#125;</span><br><span class="line"></span><br><span class="line">      <span class="keyword">return</span> getModelAndView(mavContainer, modelFactory, webRequest);</span><br><span class="line">   &#125;</span><br><span class="line">   <span class="keyword">finally</span> &#123;</span><br><span class="line">      webRequest.requestCompleted();</span><br><span class="line">   &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h4 id="什么是WebDataBinderFactory？"><a href="#什么是WebDataBinderFactory？" class="headerlink" title="什么是WebDataBinderFactory？"></a>什么是WebDataBinderFactory？</h4><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> String <span class="title">sayHello</span><span class="params">(Person person)</span></span>&#123; </span><br><span class="line"><span class="comment">//...</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<ol>
<li>假设方法里有一个Person类型的参数，Person里的属性就是age，name，sex这些的</li>
<li>前端传的参数名刚好能和Person的属性对上，WebDataBinder就会帮我们自动绑定上。</li>
</ol>
<h4 id="argumentResolvers参数解析器"><a href="#argumentResolvers参数解析器" class="headerlink" title="argumentResolvers参数解析器"></a>argumentResolvers参数解析器</h4><img src= "" data-lazy-src="https://upyunimg.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_10/image-20211020143347865.png" />



<p>注意有一些解析器，比如RequestResponseBodyMethodProcessor即是返回值解析器也是参数解析器</p>
<h4 id="returnValueHandlers返回值解析器"><a href="#returnValueHandlers返回值解析器" class="headerlink" title="returnValueHandlers返回值解析器"></a>returnValueHandlers返回值解析器</h4><img src= "" data-lazy-src="https://upyunimg.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_10/image-20211020143311546.png" />



<h3 id="15个参数解析器和27个返回值解析器是什么时候有值的？"><a href="#15个参数解析器和27个返回值解析器是什么时候有值的？" class="headerlink" title="15个参数解析器和27个返回值解析器是什么时候有值的？"></a>15个参数解析器和27个返回值解析器是什么时候有值的？</h3><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">RequestMappingHandlerAdapter</span> <span class="keyword">extends</span> <span class="title">AbstractHandlerMethodAdapter</span></span></span><br><span class="line"><span class="class">      <span class="keyword">implements</span> <span class="title">BeanFactoryAware</span>, <span class="title">InitializingBean</span> </span>&#123;</span><br><span class="line">    <span class="comment">// ...</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<p>我们可以看到RequestMappingHandlerAdapter也是实现了InitializingBean接口，所以它什么时候有值，和RequestMappingHandlerMapping是一样的逻辑</p>
<ol>
<li><code>DispatcherServlet#onRefresh()</code>开始初始化九大组件，就会开始初始化HandlerAdapter</li>
<li>首先是创建<code>DispatcherServlet.properties</code>里指定的四个HandlerAdapter实现类的对象。还是用createBean来创建HandlerAdapter的</li>
<li>其中RequestMappingHandlerAdapter创建完对象后，因为它实现了InitializingBean，所以会调用RequestMappingHandlerAdapter#afterPropertiesSet()</li>
<li>在afterPropertiesSet()中就直接new了所有默认的参数解析器和返回值解析器</li>
</ol>
<h4 id="RequestMappingHandlerAdapter-afterPropertiesSet"><a href="#RequestMappingHandlerAdapter-afterPropertiesSet" class="headerlink" title="RequestMappingHandlerAdapter#afterPropertiesSet()"></a>RequestMappingHandlerAdapter#afterPropertiesSet()</h4><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">afterPropertiesSet</span><span class="params">()</span> </span>&#123; <span class="comment">//初始化以后</span></span><br><span class="line">   <span class="comment">// Do this first, it may add ResponseBody advice beans</span></span><br><span class="line">   initControllerAdviceCache();  <span class="comment">//初始化 ControllerAdvice 【异常处理相关的功能】</span></span><br><span class="line"></span><br><span class="line">   <span class="keyword">if</span> (<span class="keyword">this</span>.argumentResolvers == <span class="keyword">null</span>) &#123;  <span class="comment">//拿到底层所有的默认 argumentResolvers</span></span><br><span class="line">      List&lt;HandlerMethodArgumentResolver&gt; resolvers = getDefaultArgumentResolvers();</span><br><span class="line">      <span class="keyword">this</span>.argumentResolvers = <span class="keyword">new</span> HandlerMethodArgumentResolverComposite().addResolvers(resolvers); <span class="comment">//把这些resolver统一组合到一个对象里面，方便管控</span></span><br><span class="line">   &#125;</span><br><span class="line">   <span class="keyword">if</span> (<span class="keyword">this</span>.initBinderArgumentResolvers == <span class="keyword">null</span>) &#123;</span><br><span class="line">      List&lt;HandlerMethodArgumentResolver&gt; resolvers = getDefaultInitBinderArgumentResolvers();</span><br><span class="line">      <span class="keyword">this</span>.initBinderArgumentResolvers = <span class="keyword">new</span> HandlerMethodArgumentResolverComposite().addResolvers(resolvers);</span><br><span class="line">   &#125;</span><br><span class="line">   <span class="keyword">if</span> (<span class="keyword">this</span>.returnValueHandlers == <span class="keyword">null</span>) &#123;</span><br><span class="line">      List&lt;HandlerMethodReturnValueHandler&gt; handlers = getDefaultReturnValueHandlers();</span><br><span class="line">      <span class="keyword">this</span>.returnValueHandlers = <span class="keyword">new</span> HandlerMethodReturnValueHandlerComposite().addHandlers(handlers);</span><br><span class="line">   &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h4 id="RequestMappingHandlerAdapter-getDefaultArgumentResolvers"><a href="#RequestMappingHandlerAdapter-getDefaultArgumentResolvers" class="headerlink" title="RequestMappingHandlerAdapter#getDefaultArgumentResolvers()"></a>RequestMappingHandlerAdapter#getDefaultArgumentResolvers()</h4><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">private</span> List&lt;HandlerMethodArgumentResolver&gt; <span class="title">getDefaultArgumentResolvers</span><span class="params">()</span> </span>&#123;</span><br><span class="line">   List&lt;HandlerMethodArgumentResolver&gt; resolvers = <span class="keyword">new</span> ArrayList&lt;&gt;(<span class="number">30</span>);</span><br><span class="line"></span><br><span class="line">   <span class="comment">// Annotation-based argument resolution</span></span><br><span class="line">   <span class="comment">//这里放了一个RequestParamMethodArgumentResolver，但是它的第二个参数是false，第二个参数是什么呢？我们后面说它的时候再讲</span></span><br><span class="line">   resolvers.add(<span class="keyword">new</span> RequestParamMethodArgumentResolver(getBeanFactory(), <span class="keyword">false</span>));</span><br><span class="line">   resolvers.add(<span class="keyword">new</span> RequestParamMapMethodArgumentResolver());</span><br><span class="line">   resolvers.add(<span class="keyword">new</span> PathVariableMethodArgumentResolver());</span><br><span class="line">   resolvers.add(<span class="keyword">new</span> PathVariableMapMethodArgumentResolver());</span><br><span class="line">   resolvers.add(<span class="keyword">new</span> MatrixVariableMethodArgumentResolver());</span><br><span class="line">   resolvers.add(<span class="keyword">new</span> MatrixVariableMapMethodArgumentResolver());</span><br><span class="line">   resolvers.add(<span class="keyword">new</span> ServletModelAttributeMethodProcessor(<span class="keyword">false</span>));</span><br><span class="line">   resolvers.add(<span class="keyword">new</span> RequestResponseBodyMethodProcessor(getMessageConverters(), <span class="keyword">this</span>.requestResponseBodyAdvice));</span><br><span class="line">   resolvers.add(<span class="keyword">new</span> RequestPartMethodArgumentResolver(getMessageConverters(), <span class="keyword">this</span>.requestResponseBodyAdvice));</span><br><span class="line">   resolvers.add(<span class="keyword">new</span> RequestHeaderMethodArgumentResolver(getBeanFactory()));</span><br><span class="line">   resolvers.add(<span class="keyword">new</span> RequestHeaderMapMethodArgumentResolver());</span><br><span class="line">   resolvers.add(<span class="keyword">new</span> ServletCookieValueMethodArgumentResolver(getBeanFactory()));</span><br><span class="line">   resolvers.add(<span class="keyword">new</span> ExpressionValueMethodArgumentResolver(getBeanFactory()));</span><br><span class="line">   resolvers.add(<span class="keyword">new</span> SessionAttributeMethodArgumentResolver());</span><br><span class="line">   resolvers.add(<span class="keyword">new</span> RequestAttributeMethodArgumentResolver());</span><br><span class="line"></span><br><span class="line">   <span class="comment">// Type-based argument resolution</span></span><br><span class="line">   resolvers.add(<span class="keyword">new</span> ServletRequestMethodArgumentResolver());</span><br><span class="line">   resolvers.add(<span class="keyword">new</span> ServletResponseMethodArgumentResolver());</span><br><span class="line">   resolvers.add(<span class="keyword">new</span> HttpEntityMethodProcessor(getMessageConverters(), <span class="keyword">this</span>.requestResponseBodyAdvice));</span><br><span class="line">   resolvers.add(<span class="keyword">new</span> RedirectAttributesMethodArgumentResolver());</span><br><span class="line">   resolvers.add(<span class="keyword">new</span> ModelMethodProcessor());</span><br><span class="line">   resolvers.add(<span class="keyword">new</span> MapMethodProcessor());</span><br><span class="line">   resolvers.add(<span class="keyword">new</span> ErrorsMethodArgumentResolver());</span><br><span class="line">   resolvers.add(<span class="keyword">new</span> SessionStatusMethodArgumentResolver());</span><br><span class="line">   resolvers.add(<span class="keyword">new</span> UriComponentsBuilderMethodArgumentResolver());</span><br><span class="line">   <span class="keyword">if</span> (KotlinDetector.isKotlinPresent()) &#123;</span><br><span class="line">      resolvers.add(<span class="keyword">new</span> ContinuationHandlerMethodArgumentResolver());</span><br><span class="line">   &#125;</span><br><span class="line"></span><br><span class="line">   <span class="comment">// Custom arguments</span></span><br><span class="line">   <span class="keyword">if</span> (getCustomArgumentResolvers() != <span class="keyword">null</span>) &#123;</span><br><span class="line">      resolvers.addAll(getCustomArgumentResolvers());</span><br><span class="line">   &#125;</span><br><span class="line"></span><br><span class="line">   <span class="comment">// Catch-all</span></span><br><span class="line">   resolvers.add(<span class="keyword">new</span> PrincipalMethodArgumentResolver());</span><br><span class="line">   <span class="comment">//这里也放了一个RequestParamMethodArgumentResolver，但是它的第二个参数是true</span></span><br><span class="line">   resolvers.add(<span class="keyword">new</span> RequestParamMethodArgumentResolver(getBeanFactory(), <span class="keyword">true</span>));</span><br><span class="line">   resolvers.add(<span class="keyword">new</span> ServletModelAttributeMethodProcessor(<span class="keyword">true</span>));</span><br><span class="line"></span><br><span class="line">   <span class="keyword">return</span> resolvers;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h4 id="RequestMappingHandlerAdapter-getDefaultReturnValueHandlers"><a href="#RequestMappingHandlerAdapter-getDefaultReturnValueHandlers" class="headerlink" title="RequestMappingHandlerAdapter#getDefaultReturnValueHandlers()"></a>RequestMappingHandlerAdapter#getDefaultReturnValueHandlers()</h4><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">private</span> List&lt;HandlerMethodReturnValueHandler&gt; <span class="title">getDefaultReturnValueHandlers</span><span class="params">()</span> </span>&#123;</span><br><span class="line">   List&lt;HandlerMethodReturnValueHandler&gt; handlers = <span class="keyword">new</span> ArrayList&lt;&gt;(<span class="number">20</span>);</span><br><span class="line"></span><br><span class="line">   <span class="comment">// Single-purpose return value types</span></span><br><span class="line">   handlers.add(<span class="keyword">new</span> ModelAndViewMethodReturnValueHandler());</span><br><span class="line">   handlers.add(<span class="keyword">new</span> ModelMethodProcessor());</span><br><span class="line">   handlers.add(<span class="keyword">new</span> ViewMethodReturnValueHandler());</span><br><span class="line">   handlers.add(<span class="keyword">new</span> ResponseBodyEmitterReturnValueHandler(getMessageConverters(),</span><br><span class="line">         <span class="keyword">this</span>.reactiveAdapterRegistry, <span class="keyword">this</span>.taskExecutor, <span class="keyword">this</span>.contentNegotiationManager));</span><br><span class="line">   handlers.add(<span class="keyword">new</span> StreamingResponseBodyReturnValueHandler());</span><br><span class="line">   handlers.add(<span class="keyword">new</span> HttpEntityMethodProcessor(getMessageConverters(),</span><br><span class="line">         <span class="keyword">this</span>.contentNegotiationManager, <span class="keyword">this</span>.requestResponseBodyAdvice));</span><br><span class="line">   handlers.add(<span class="keyword">new</span> HttpHeadersReturnValueHandler());</span><br><span class="line">   handlers.add(<span class="keyword">new</span> CallableMethodReturnValueHandler());</span><br><span class="line">   handlers.add(<span class="keyword">new</span> DeferredResultMethodReturnValueHandler());</span><br><span class="line">   handlers.add(<span class="keyword">new</span> AsyncTaskMethodReturnValueHandler(<span class="keyword">this</span>.beanFactory));</span><br><span class="line"></span><br><span class="line">   <span class="comment">// Annotation-based return value types</span></span><br><span class="line">   handlers.add(<span class="keyword">new</span> ServletModelAttributeMethodProcessor(<span class="keyword">false</span>));</span><br><span class="line">   handlers.add(<span class="keyword">new</span> RequestResponseBodyMethodProcessor(getMessageConverters(),</span><br><span class="line">         <span class="keyword">this</span>.contentNegotiationManager, <span class="keyword">this</span>.requestResponseBodyAdvice));</span><br><span class="line"></span><br><span class="line">   <span class="comment">// Multi-purpose return value types</span></span><br><span class="line">   handlers.add(<span class="keyword">new</span> ViewNameMethodReturnValueHandler());</span><br><span class="line">   handlers.add(<span class="keyword">new</span> MapMethodProcessor());</span><br><span class="line"></span><br><span class="line">   <span class="comment">// Custom return value types</span></span><br><span class="line">   <span class="keyword">if</span> (getCustomReturnValueHandlers() != <span class="keyword">null</span>) &#123;</span><br><span class="line">      handlers.addAll(getCustomReturnValueHandlers());</span><br><span class="line">   &#125;</span><br><span class="line"></span><br><span class="line">   <span class="comment">// Catch-all</span></span><br><span class="line">   <span class="keyword">if</span> (!CollectionUtils.isEmpty(getModelAndViewResolvers())) &#123;</span><br><span class="line">      handlers.add(<span class="keyword">new</span> ModelAndViewResolverMethodReturnValueHandler(getModelAndViewResolvers()));</span><br><span class="line">   &#125;</span><br><span class="line">   <span class="keyword">else</span> &#123;</span><br><span class="line">      handlers.add(<span class="keyword">new</span> ServletModelAttributeMethodProcessor(<span class="keyword">true</span>));</span><br><span class="line">   &#125;</span><br><span class="line"></span><br><span class="line">   <span class="keyword">return</span> handlers;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>









<h2 id="RequestMappingHandlerAdapter中的参数解析器工作流程"><a href="#RequestMappingHandlerAdapter中的参数解析器工作流程" class="headerlink" title="RequestMappingHandlerAdapter中的参数解析器工作流程"></a>RequestMappingHandlerAdapter中的参数解析器工作流程</h2><h3 id="DispatcherServlet-doDispatch-3"><a href="#DispatcherServlet-doDispatch-3" class="headerlink" title="DispatcherServlet#doDispatch()"></a>DispatcherServlet#doDispatch()</h3><img src= "" data-lazy-src="https://upyunimg.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_10/image-20211020153627726.png" />



<p><code>AbstractHandlerMethodAdapter#handle(HttpServletRequest , HttpServletResponse , Object )</code>和<code>RequestMappingHandlerAdapter#handleInternal(HttpServletRequest , HttpServletResponse , HandlerMethod )</code>上面说过，跳过</p>
<h3 id="RequestMappingHandlerAdapter-invokeHandlerMethod-准备执行目标方法"><a href="#RequestMappingHandlerAdapter-invokeHandlerMethod-准备执行目标方法" class="headerlink" title="RequestMappingHandlerAdapter#invokeHandlerMethod()准备执行目标方法"></a>RequestMappingHandlerAdapter#invokeHandlerMethod()准备执行目标方法</h3><img src= "" data-lazy-src="https://upyunimg.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_10/image-20211020153939442.png"  />





<h3 id="ServletInvocableHandlerMethod-invokeAndHandle-真正开始执行目标方法"><a href="#ServletInvocableHandlerMethod-invokeAndHandle-真正开始执行目标方法" class="headerlink" title="ServletInvocableHandlerMethod#invokeAndHandle()真正开始执行目标方法"></a>ServletInvocableHandlerMethod#invokeAndHandle()真正开始执行目标方法</h3><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">invokeAndHandle</span><span class="params">(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,</span></span></span><br><span class="line"><span class="function"><span class="params">      Object... providedArgs)</span> <span class="keyword">throws</span> Exception </span>&#123;</span><br><span class="line">   <span class="comment">//目标方法的反射执行过程</span></span><br><span class="line">   Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);</span><br><span class="line">   setResponseStatus(webRequest);</span><br><span class="line"></span><br><span class="line">   <span class="keyword">if</span> (returnValue == <span class="keyword">null</span>) &#123;</span><br><span class="line">      <span class="keyword">if</span> (isRequestNotModified(webRequest) || getResponseStatus() != <span class="keyword">null</span> || mavContainer.isRequestHandled()) &#123;</span><br><span class="line">         disableContentCachingIfNecessary(webRequest);</span><br><span class="line">         mavContainer.setRequestHandled(<span class="keyword">true</span>);</span><br><span class="line">         <span class="keyword">return</span>;</span><br><span class="line">      &#125;</span><br><span class="line">   &#125;</span><br><span class="line">   <span class="keyword">else</span> <span class="keyword">if</span> (StringUtils.hasText(getResponseStatusReason())) &#123;</span><br><span class="line">      mavContainer.setRequestHandled(<span class="keyword">true</span>);</span><br><span class="line">      <span class="keyword">return</span>;</span><br><span class="line">   &#125;</span><br><span class="line"></span><br><span class="line">   mavContainer.setRequestHandled(<span class="keyword">false</span>);</span><br><span class="line">   Assert.state(<span class="keyword">this</span>.returnValueHandlers != <span class="keyword">null</span>, <span class="string">&quot;No return value handlers&quot;</span>);</span><br><span class="line">   <span class="keyword">try</span> &#123;</span><br><span class="line">      <span class="keyword">this</span>.returnValueHandlers.handleReturnValue(</span><br><span class="line">            returnValue, getReturnValueType(returnValue), mavContainer, webRequest);</span><br><span class="line">   &#125;</span><br><span class="line">   <span class="keyword">catch</span> (Exception ex) &#123;</span><br><span class="line">      <span class="keyword">if</span> (logger.isTraceEnabled()) &#123;</span><br><span class="line">         logger.trace(formatErrorForReturnValue(returnValue), ex);</span><br><span class="line">      &#125;</span><br><span class="line">      <span class="keyword">throw</span> ex;</span><br><span class="line">   &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<h3 id="InvocableHandlerMethod-invokeForRequest-目标方法的反射执行过程"><a href="#InvocableHandlerMethod-invokeForRequest-目标方法的反射执行过程" class="headerlink" title="InvocableHandlerMethod#invokeForRequest( )目标方法的反射执行过程"></a>InvocableHandlerMethod#invokeForRequest( )目标方法的反射执行过程</h3><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> Object <span class="title">invokeForRequest</span><span class="params">(NativeWebRequest request, <span class="meta">@Nullable</span> ModelAndViewContainer mavContainer,</span></span></span><br><span class="line"><span class="function"><span class="params">      Object... providedArgs)</span> <span class="keyword">throws</span> Exception </span>&#123;</span><br><span class="line">   <span class="comment">//获取方法的请求参数</span></span><br><span class="line">   Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);</span><br><span class="line">   <span class="keyword">if</span> (logger.isTraceEnabled()) &#123;</span><br><span class="line">      logger.trace(<span class="string">&quot;Arguments: &quot;</span> + Arrays.toString(args));</span><br><span class="line">   &#125;</span><br><span class="line">   <span class="keyword">return</span> doInvoke(args); <span class="comment">//就是反射执行</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<img src= "" data-lazy-src="https://upyunimg.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_10/image-20211020154445639.png" />

<p>开始进入正题</p>
<h3 id="InvocableHandlerMethod-getMethodArgumentValues-获取方法的请求参数"><a href="#InvocableHandlerMethod-getMethodArgumentValues-获取方法的请求参数" class="headerlink" title="InvocableHandlerMethod#getMethodArgumentValues()获取方法的请求参数"></a>InvocableHandlerMethod#getMethodArgumentValues()获取方法的请求参数</h3><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">protected</span> Object[] getMethodArgumentValues(NativeWebRequest request, <span class="meta">@Nullable</span> ModelAndViewContainer mavContainer,</span><br><span class="line">      Object... providedArgs) <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">   <span class="comment">//直接拿到方法的所有参数,getMethodParameters是HandlerMethod类的，在之前构造handler的时候就已经通过反射将相关信息保存好了</span></span><br><span class="line">   <span class="comment">// 我们之前讲过标注@RequestMapping注解的handler最终会被封装成HandlerMethod</span></span><br><span class="line">   MethodParameter[] parameters = getMethodParameters();</span><br><span class="line">   <span class="keyword">if</span> (ObjectUtils.isEmpty(parameters)) &#123;</span><br><span class="line">      <span class="keyword">return</span> EMPTY_ARGS;</span><br><span class="line">   &#125;</span><br><span class="line">   <span class="comment">//准备args数组（和parameters一样长），挨个确定每个参数都是什么值</span></span><br><span class="line">   Object[] args = <span class="keyword">new</span> Object[parameters.length];</span><br><span class="line">   <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; parameters.length; i++) &#123;</span><br><span class="line">      MethodParameter parameter = parameters[i];</span><br><span class="line">      parameter.initParameterNameDiscovery(<span class="keyword">this</span>.parameterNameDiscoverer);</span><br><span class="line">      args[i] = findProvidedArgument(parameter, providedArgs); <span class="comment">//先去已提供的参数中找</span></span><br><span class="line">      <span class="keyword">if</span> (args[i] != <span class="keyword">null</span>) &#123;</span><br><span class="line">         <span class="keyword">continue</span>;</span><br><span class="line">      &#125;</span><br><span class="line">      <span class="keyword">if</span> (!<span class="keyword">this</span>.resolvers.supportsParameter(parameter)) &#123; <span class="comment">//支持这种参数的解析器也会被放到缓存，下一次进来，就不用27个人挨个判断</span></span><br><span class="line">         <span class="keyword">throw</span> <span class="keyword">new</span> IllegalStateException(formatArgumentError(parameter, <span class="string">&quot;No suitable resolver&quot;</span>));</span><br><span class="line">      &#125;</span><br><span class="line">      <span class="keyword">try</span> &#123;</span><br><span class="line">         args[i] = <span class="keyword">this</span>.resolvers.resolveArgument(parameter, mavContainer, request, <span class="keyword">this</span>.dataBinderFactory);</span><br><span class="line">      &#125;</span><br><span class="line">      <span class="keyword">catch</span> (Exception ex) &#123;</span><br><span class="line">         <span class="comment">// Leave stack trace for later, exception may actually be resolved and handled...</span></span><br><span class="line">         <span class="keyword">if</span> (logger.isDebugEnabled()) &#123;</span><br><span class="line">            String exMsg = ex.getMessage();</span><br><span class="line">            <span class="keyword">if</span> (exMsg != <span class="keyword">null</span> &amp;&amp; !exMsg.contains(parameter.getExecutable().toGenericString())) &#123;</span><br><span class="line">               logger.debug(formatArgumentError(parameter, exMsg));</span><br><span class="line">            &#125;</span><br><span class="line">         &#125;</span><br><span class="line">         <span class="keyword">throw</span> ex;</span><br><span class="line">      &#125;</span><br><span class="line">   &#125;</span><br><span class="line">   <span class="keyword">return</span> args;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<img src= "" data-lazy-src="https://upyunimg.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_10/image-20211020160234158.png" />

<h3 id="HandlerMethodArgumentResolverComposite准备循环27个参数解析器"><a href="#HandlerMethodArgumentResolverComposite准备循环27个参数解析器" class="headerlink" title="HandlerMethodArgumentResolverComposite准备循环27个参数解析器"></a>HandlerMethodArgumentResolverComposite准备循环27个参数解析器</h3><img src= "" data-lazy-src="https://upyunimg.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_10/image-20211020160936355.png" />

<h3 id="HandlerMethodArgumentResolverComposite-supportsParameter"><a href="#HandlerMethodArgumentResolverComposite-supportsParameter" class="headerlink" title="HandlerMethodArgumentResolverComposite#supportsParameter()"></a>HandlerMethodArgumentResolverComposite#supportsParameter()</h3><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">supportsParameter</span><span class="params">(MethodParameter parameter)</span> </span>&#123;</span><br><span class="line">   <span class="keyword">return</span> getArgumentResolver(parameter) != <span class="keyword">null</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h3 id="HandlerMethodArgumentResolverComposite-getArgumentResolver-循环判断哪个参数解析器支持这个参数"><a href="#HandlerMethodArgumentResolverComposite-getArgumentResolver-循环判断哪个参数解析器支持这个参数" class="headerlink" title="HandlerMethodArgumentResolverComposite#getArgumentResolver()循环判断哪个参数解析器支持这个参数"></a>HandlerMethodArgumentResolverComposite#getArgumentResolver()循环判断哪个参数解析器支持这个参数</h3><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">private</span> HandlerMethodArgumentResolver <span class="title">getArgumentResolver</span><span class="params">(MethodParameter parameter)</span> </span>&#123;</span><br><span class="line">   HandlerMethodArgumentResolver result = <span class="keyword">this</span>.argumentResolverCache.get(parameter); <span class="comment">//先看缓存中有没有</span></span><br><span class="line">   <span class="keyword">if</span> (result == <span class="keyword">null</span>) &#123;</span><br><span class="line">      <span class="keyword">for</span> (HandlerMethodArgumentResolver resolver : <span class="keyword">this</span>.argumentResolvers) &#123;</span><br><span class="line">         <span class="keyword">if</span> (resolver.supportsParameter(parameter)) &#123;</span><br><span class="line">            result = resolver;</span><br><span class="line">            <span class="keyword">this</span>.argumentResolverCache.put(parameter, result);</span><br><span class="line">            <span class="keyword">break</span>;</span><br><span class="line">         &#125;</span><br><span class="line">      &#125;</span><br><span class="line">   &#125;</span><br><span class="line">   <span class="keyword">return</span> result;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>这里就是循环27个参数解析器，看哪一个能解析这种类型的参数。因为参数解析器太多，我们就举几个常用的例子</p>
<h3 id="RequestParamMethodArgumentResolver判断-RequestParam注解的参数解析器"><a href="#RequestParamMethodArgumentResolver判断-RequestParam注解的参数解析器" class="headerlink" title="RequestParamMethodArgumentResolver判断@RequestParam注解的参数解析器"></a>RequestParamMethodArgumentResolver判断@RequestParam注解的参数解析器</h3><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">package</span> org.springframework.web.method.annotation</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">supportsParameter</span><span class="params">(MethodParameter parameter)</span> </span>&#123;</span><br><span class="line">       <span class="keyword">if</span> (parameter.hasParameterAnnotation(RequestParam.class)) &#123;</span><br><span class="line">          <span class="comment">//注意这里是为了不跟下面的RequestParamMapMethodArgumentResolver产生冲突</span></span><br><span class="line">          <span class="keyword">if</span> (Map.class.isAssignableFrom(parameter.nestedIfOptional().getNestedParameterType())) &#123;</span><br><span class="line">             RequestParam requestParam = parameter.getParameterAnnotation(RequestParam.class);</span><br><span class="line">             <span class="keyword">return</span> (requestParam != <span class="keyword">null</span> &amp;&amp; StringUtils.hasText(requestParam.name()));</span><br><span class="line">          &#125;</span><br><span class="line">          <span class="keyword">else</span> &#123;</span><br><span class="line">             <span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line">          &#125;</span><br><span class="line">       &#125;</span><br><span class="line">       <span class="keyword">else</span> &#123;</span><br><span class="line">          <span class="keyword">if</span> (parameter.hasParameterAnnotation(RequestPart.class)) &#123;</span><br><span class="line">             <span class="keyword">return</span> <span class="keyword">false</span>;</span><br><span class="line">          &#125;</span><br><span class="line">          parameter = parameter.nestedIfOptional();</span><br><span class="line">          <span class="keyword">if</span> (MultipartResolutionDelegate.isMultipartArgument(parameter)) &#123;</span><br><span class="line">             <span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line">          &#125;</span><br><span class="line">          <span class="keyword">else</span> <span class="keyword">if</span> (<span class="keyword">this</span>.useDefaultResolution) &#123;</span><br><span class="line">             <span class="keyword">return</span> BeanUtils.isSimpleProperty(parameter.getNestedParameterType());</span><br><span class="line">          &#125;</span><br><span class="line">          <span class="keyword">else</span> &#123;</span><br><span class="line">             <span class="keyword">return</span> <span class="keyword">false</span>;</span><br><span class="line">          &#125;</span><br><span class="line">       &#125;</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure>

<p>这个就是判断参数有没有标@RequestParam注解</p>
<p>注意这个是org.springframework.web.method.annotation包下的</p>
<img src= "" data-lazy-src="https://upyunimg.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_10/image-20211020164639063.png"/>

<img src= "" data-lazy-src="https://upyunimg.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_10/image-20211020164438763.png"  />

<ol>
<li>第一次进入<code>RequestParamMethodArgumentResolver#supportsParameter(MethodParameter parameter)</code>时，我们在前面讲过，在<code>RequestMappingHandlerAdapter#getDefaultArgumentResolvers()</code>添加默认解析器的时候，spring往参数解析器里加了两个</li>
</ol>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="comment">//这里放了一个RequestParamMethodArgumentResolver，但是它的第二个参数是false，第二个参数是什么呢？我们后面说它的时候再讲</span></span><br><span class="line">    resolvers.add(<span class="keyword">new</span> RequestParamMethodArgumentResolver(getBeanFactory(), <span class="keyword">false</span>));</span><br><span class="line">    </span><br><span class="line">     <span class="comment">//这里也放了一个RequestParamMethodArgumentResolver，但是它的第二个参数是true</span></span><br><span class="line">    resolvers.add(<span class="keyword">new</span> RequestParamMethodArgumentResolver(getBeanFactory(), <span class="keyword">true</span>));</span><br></pre></td></tr></table></figure>

<ol start="2">
<li><p>这里的true和false指定的就是useDefaultResolution这个东西</p>
</li>
<li><p>然后我们验证一下第二个RequestParamMethodArgumentResolver</p>
</li>
</ol>
<img src= "" data-lazy-src="https://upyunimg.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_10/image-20211020165119933.png" />

<ol start="4">
<li>useDefaultResolution这个看名字应该就是前面25个处理器都用不到的时候，就用这个默认中的默认处理器第26个<code>RequestParamMethodArgumentResolver</code>(双重默认)</li>
<li>那什么时候用第27个<code>ServletModelAttributeMethodProcessor</code>这个处理器呢？当第26个在上面那几个if else中返回false，就会用最后这个，至于什么时候返回false，我没有深入研究。</li>
</ol>
<h3 id="RequestParamMapMethodArgumentResolver参数解析器"><a href="#RequestParamMapMethodArgumentResolver参数解析器" class="headerlink" title="RequestParamMapMethodArgumentResolver参数解析器"></a>RequestParamMapMethodArgumentResolver参数解析器</h3><blockquote>
<p><code>@RequestParam Map&lt;String,Object&gt; params</code></p>
</blockquote>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">supportsParameter</span><span class="params">(MethodParameter parameter)</span> </span>&#123;</span><br><span class="line">   RequestParam requestParam = parameter.getParameterAnnotation(RequestParam.class);</span><br><span class="line">   <span class="keyword">return</span> (requestParam != <span class="keyword">null</span> &amp;&amp; Map.class.isAssignableFrom(parameter.getParameterType()) &amp;&amp;</span><br><span class="line">         !StringUtils.hasText(requestParam.name()));</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>判断有没有标@RequestParam注解，并且类型是Map</p>
<h3 id="PathVariableMethodArgumentResolver判断-PathVariable注解的参数解析器"><a href="#PathVariableMethodArgumentResolver判断-PathVariable注解的参数解析器" class="headerlink" title="PathVariableMethodArgumentResolver判断@PathVariable注解的参数解析器"></a>PathVariableMethodArgumentResolver判断@PathVariable注解的参数解析器</h3><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">supportsParameter</span><span class="params">(MethodParameter parameter)</span> </span>&#123;</span><br><span class="line">   <span class="keyword">if</span> (!parameter.hasParameterAnnotation(PathVariable.class)) &#123;</span><br><span class="line">      <span class="keyword">return</span> <span class="keyword">false</span>;</span><br><span class="line">   &#125;</span><br><span class="line">   <span class="keyword">if</span> (Map.class.isAssignableFrom(parameter.nestedIfOptional().getNestedParameterType())) &#123;</span><br><span class="line">      PathVariable pathVariable = parameter.getParameterAnnotation(PathVariable.class);</span><br><span class="line">      <span class="keyword">return</span> (pathVariable != <span class="keyword">null</span> &amp;&amp; StringUtils.hasText(pathVariable.value()));</span><br><span class="line">   &#125;</span><br><span class="line">   <span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>相信大家已经能猜到了，这里就是判断参数上有没有标注@PathVariable注解</p>
<h3 id="返回到InvocableHandlerMethod-getMethodArgumentValues"><a href="#返回到InvocableHandlerMethod-getMethodArgumentValues" class="headerlink" title="返回到InvocableHandlerMethod#getMethodArgumentValues( )"></a>返回到InvocableHandlerMethod#getMethodArgumentValues( )</h3><img src= "" data-lazy-src="https://upyunimg.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_10/image-20211020170650734.png" />



<p>具体的参数赋值过程，就是一些数据类型的转换，可以自己去看下。</p>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">args[i] &#x3D; this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);</span><br></pre></td></tr></table></figure>





<h3 id="返回到InvocableHandlerMethod-invokeForRequest"><a href="#返回到InvocableHandlerMethod-invokeForRequest" class="headerlink" title="返回到InvocableHandlerMethod#invokeForRequest()"></a>返回到InvocableHandlerMethod#invokeForRequest()</h3><img src= "" data-lazy-src="https://upyunimg.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_10/image-20211020171415141.png"  />

<p>接下来就是交给反射去执行</p>
<h3 id="返回到ServletInvocableHandlerMethod-invokeAndHandle"><a href="#返回到ServletInvocableHandlerMethod-invokeAndHandle" class="headerlink" title="返回到ServletInvocableHandlerMethod#invokeAndHandle()"></a>返回到ServletInvocableHandlerMethod#invokeAndHandle()</h3><img src= "" data-lazy-src="https://upyunimg.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_10/image-20211020171844159.png" />

<p>拿到返回值了，接下来交给返回值处理器</p>
<h3 id="SpringMVC到底能写哪些参数？"><a href="#SpringMVC到底能写哪些参数？" class="headerlink" title="SpringMVC到底能写哪些参数？"></a>SpringMVC到底能写哪些参数？</h3><p>详见<a target="_blank" rel="noopener" href="https://docs.spring.io/spring-framework/docs/current/reference/html/web.html#mvc-ann-arguments">官方文档-参数相关</a></p>
<h2 id="RequestMappingHandlerAdapter中的返回值解析器工作流程"><a href="#RequestMappingHandlerAdapter中的返回值解析器工作流程" class="headerlink" title="RequestMappingHandlerAdapter中的返回值解析器工作流程"></a>RequestMappingHandlerAdapter中的返回值解析器工作流程</h2><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="meta">@Controller</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">HelloController</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">	<span class="function"><span class="keyword">public</span> <span class="title">HelloController</span><span class="params">()</span> </span>&#123;</span><br><span class="line">		System.out.println(<span class="string">&quot;HelloController.....&quot;</span>);</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	<span class="meta">@Autowired</span></span><br><span class="line">	HelloService helloService;</span><br><span class="line"></span><br><span class="line">	<span class="meta">@GetMapping(&quot;/hello&quot;)</span> </span><br><span class="line">	<span class="function"><span class="keyword">public</span> String <span class="title">sayHello</span><span class="params">(String name, </span></span></span><br><span class="line"><span class="function"><span class="params">						   <span class="meta">@RequestParam(&quot;user&quot;)</span> String user,</span></span></span><br><span class="line"><span class="function"><span class="params">						   HttpSession session, HttpServletRequest request, //原生的session对象</span></span></span><br><span class="line"><span class="function"><span class="params">						   <span class="meta">@RequestHeader(&quot;User-Agent&quot;)</span> String ua)</span> </span>&#123; <span class="comment">//@RequestParam Map&lt;String,Object&gt; params：所有请求参数全封装进来</span></span><br><span class="line">		<span class="comment">// @RequestHeader(&quot;User-Agent&quot;) String  ua 获取指定请求头的值</span></span><br><span class="line">		String header = request.getHeader(<span class="string">&quot;User-Agent&quot;</span>);</span><br><span class="line">		<span class="comment">//方法的签名，到底能写那些？</span></span><br><span class="line">		<span class="comment">//详细参照 https://docs.spring.io/spring-framework/docs/current/reference/html/web.html#mvc-ann-arguments</span></span><br><span class="line">		<span class="comment">//https://www.bilibili.com/video/BV19K4y1L7MT?p=32</span></span><br><span class="line">		String mvc = helloService.say(user + <span class="string">&quot;：MVC&quot;</span> + name);</span><br><span class="line">		session.setAttribute(<span class="string">&quot;msg&quot;</span>, mvc);</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">		<span class="comment">//SpringMVC的目标方法能写哪些返回值</span></span><br><span class="line">		<span class="comment">//https://docs.spring.io/spring-framework/docs/current/reference/html/web.html#mvc-ann-return-types</span></span><br><span class="line">		<span class="keyword">return</span> <span class="string">&quot;index.jsp&quot;</span>;</span><br><span class="line">	&#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure>





<h3 id="ServletInvocableHandlerMethod-invokeAndHandle"><a href="#ServletInvocableHandlerMethod-invokeAndHandle" class="headerlink" title="ServletInvocableHandlerMethod#invokeAndHandle()"></a>ServletInvocableHandlerMethod#invokeAndHandle()</h3><img src= "" data-lazy-src="https://upyunimg.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_10/image-20211020175522276.png"/>

<p>我们可以看到RequestResponseBodyMethodProcessor比ViewNameMethodReturnValueHandler优先级高，接下来我们细讲</p>
<h3 id="HandlerMethodReturnValueHandlerComposite-handleReturnValue"><a href="#HandlerMethodReturnValueHandlerComposite-handleReturnValue" class="headerlink" title="HandlerMethodReturnValueHandlerComposite#handleReturnValue()"></a>HandlerMethodReturnValueHandlerComposite#handleReturnValue()</h3><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="meta">@Override</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">handleReturnValue</span><span class="params">(<span class="meta">@Nullable</span> Object returnValue, MethodParameter returnType,</span></span></span><br><span class="line"><span class="function"><span class="params">      ModelAndViewContainer mavContainer, NativeWebRequest webRequest)</span> <span class="keyword">throws</span> Exception </span>&#123;</span><br><span class="line">   <span class="comment">//找到合适的返回值处理器.</span></span><br><span class="line">   HandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType);</span><br><span class="line">   <span class="keyword">if</span> (handler == <span class="keyword">null</span>) &#123;</span><br><span class="line">      <span class="keyword">throw</span> <span class="keyword">new</span> IllegalArgumentException(<span class="string">&quot;Unknown return value type: &quot;</span> + returnType.getParameterType().getName());</span><br><span class="line">   &#125; <span class="comment">//返回值处理器. 处理返回值</span></span><br><span class="line">   handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h3 id="HandlerMethodReturnValueHandlerComposite-selectHandler-循环15个返回值处理器找到合适的返回值处理器"><a href="#HandlerMethodReturnValueHandlerComposite-selectHandler-循环15个返回值处理器找到合适的返回值处理器" class="headerlink" title="HandlerMethodReturnValueHandlerComposite#selectHandler()循环15个返回值处理器找到合适的返回值处理器"></a>HandlerMethodReturnValueHandlerComposite#selectHandler()循环15个返回值处理器找到合适的返回值处理器</h3><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">private</span> HandlerMethodReturnValueHandler <span class="title">selectHandler</span><span class="params">(<span class="meta">@Nullable</span> Object value, MethodParameter returnType)</span> </span>&#123;</span><br><span class="line">   <span class="keyword">boolean</span> isAsyncValue = isAsyncReturnValue(value, returnType);</span><br><span class="line">   <span class="comment">//还是老样子for循环</span></span><br><span class="line">   <span class="keyword">for</span> (HandlerMethodReturnValueHandler handler : <span class="keyword">this</span>.returnValueHandlers) &#123;</span><br><span class="line">      <span class="keyword">if</span> (isAsyncValue &amp;&amp; !(handler <span class="keyword">instanceof</span> AsyncHandlerMethodReturnValueHandler)) &#123;</span><br><span class="line">         <span class="keyword">continue</span>;</span><br><span class="line">      &#125;</span><br><span class="line">      <span class="keyword">if</span> (handler.supportsReturnType(returnType)) &#123;</span><br><span class="line">         <span class="keyword">return</span> handler;</span><br><span class="line">      &#125;</span><br><span class="line">   &#125;</span><br><span class="line">   <span class="keyword">return</span> <span class="keyword">null</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>我们就直接放行，看最终找到的是哪个</p>
<img src= "" data-lazy-src="https://upyunimg.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_10/image-20211020180845184.png"/>



<h3 id="ViewNameMethodReturnValueHandler-supportsReturnType"><a href="#ViewNameMethodReturnValueHandler-supportsReturnType" class="headerlink" title="ViewNameMethodReturnValueHandler#supportsReturnType()"></a>ViewNameMethodReturnValueHandler#supportsReturnType()</h3><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">supportsReturnType</span><span class="params">(MethodParameter returnType)</span> </span>&#123;</span><br><span class="line">   Class&lt;?&gt; paramType = returnType.getParameterType();</span><br><span class="line">   <span class="comment">//返回值是void，或者字符串</span></span><br><span class="line">   <span class="keyword">return</span> (<span class="keyword">void</span>.class == paramType || CharSequence.class.isAssignableFrom(paramType));</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<h3 id="返回到HandlerMethodReturnValueHandlerComposite-handleReturnValue"><a href="#返回到HandlerMethodReturnValueHandlerComposite-handleReturnValue" class="headerlink" title="返回到HandlerMethodReturnValueHandlerComposite#handleReturnValue()"></a>返回到HandlerMethodReturnValueHandlerComposite#handleReturnValue()</h3><img src= "" data-lazy-src="https://upyunimg.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_10/image-20211020181114886.png" />



<h3 id="ViewNameMethodReturnValueHandler-handleReturnValue-开始处理返回值"><a href="#ViewNameMethodReturnValueHandler-handleReturnValue-开始处理返回值" class="headerlink" title="ViewNameMethodReturnValueHandler#handleReturnValue()开始处理返回值"></a>ViewNameMethodReturnValueHandler#handleReturnValue()开始处理返回值</h3><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">handleReturnValue</span><span class="params">(<span class="meta">@Nullable</span> Object returnValue, MethodParameter returnType,</span></span></span><br><span class="line"><span class="function"><span class="params">      ModelAndViewContainer mavContainer, NativeWebRequest webRequest)</span> <span class="keyword">throws</span> Exception </span>&#123;</span><br><span class="line"></span><br><span class="line">   <span class="keyword">if</span> (returnValue <span class="keyword">instanceof</span> CharSequence) &#123; <span class="comment">//只要是字符串就是跳转到的页面地址</span></span><br><span class="line">      String viewName = returnValue.toString();</span><br><span class="line">      mavContainer.setViewName(viewName);</span><br><span class="line">      <span class="keyword">if</span> (isRedirectViewName(viewName)) &#123; <span class="comment">//是否是重定向的方式  redirect:</span></span><br><span class="line">         mavContainer.setRedirectModelScenario(<span class="keyword">true</span>);</span><br><span class="line">      &#125;</span><br><span class="line">   &#125;</span><br><span class="line">   <span class="keyword">else</span> <span class="keyword">if</span> (returnValue != <span class="keyword">null</span>) &#123;</span><br><span class="line">      <span class="comment">// should not happen</span></span><br><span class="line">      <span class="keyword">throw</span> <span class="keyword">new</span> UnsupportedOperationException(<span class="string">&quot;Unexpected return type: &quot;</span> +</span><br><span class="line">            returnType.getParameterType().getName() + <span class="string">&quot; in method: &quot;</span> + returnType.getMethod());</span><br><span class="line">   &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<img src= "" data-lazy-src="https://upyunimg.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_10/image-20211020181707508.png" />

<p>然后就准备返回了</p>
<h3 id="返回到RequestMappingHandlerAdapter-invokeHandlerMethod"><a href="#返回到RequestMappingHandlerAdapter-invokeHandlerMethod" class="headerlink" title="返回到RequestMappingHandlerAdapter#invokeHandlerMethod()"></a>返回到RequestMappingHandlerAdapter#invokeHandlerMethod()</h3><img src= "" data-lazy-src="https://upyunimg.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_10/image-20211020182700550.png"/>

<h3 id="RequestMappingHandlerAdapter-getModelAndView-进行视图解析相关工作"><a href="#RequestMappingHandlerAdapter-getModelAndView-进行视图解析相关工作" class="headerlink" title="RequestMappingHandlerAdapter#getModelAndView()进行视图解析相关工作"></a>RequestMappingHandlerAdapter#getModelAndView()进行视图解析相关工作</h3><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">private</span> ModelAndView <span class="title">getModelAndView</span><span class="params">(ModelAndViewContainer mavContainer,</span></span></span><br><span class="line"><span class="function"><span class="params">      ModelFactory modelFactory, NativeWebRequest webRequest)</span> <span class="keyword">throws</span> Exception </span>&#123;</span><br><span class="line">   <span class="comment">//modelFactory准备模型数据  （请求域数据共享）session里面的数据搬家到request域</span></span><br><span class="line">   modelFactory.updateModel(webRequest, mavContainer);</span><br><span class="line">   <span class="keyword">if</span> (mavContainer.isRequestHandled()) &#123;</span><br><span class="line">      <span class="keyword">return</span> <span class="keyword">null</span>;</span><br><span class="line">   &#125;</span><br><span class="line">   ModelMap model = mavContainer.getModel();</span><br><span class="line">   ModelAndView mav = <span class="keyword">new</span> ModelAndView(mavContainer.getViewName(), model, mavContainer.getStatus());</span><br><span class="line">   <span class="keyword">if</span> (!mavContainer.isViewReference()) &#123;</span><br><span class="line">      mav.setView((View) mavContainer.getView());</span><br><span class="line">   &#125;</span><br><span class="line">   <span class="keyword">if</span> (model <span class="keyword">instanceof</span> RedirectAttributes) &#123;</span><br><span class="line">      Map&lt;String, ?&gt; flashAttributes = ((RedirectAttributes) model).getFlashAttributes();</span><br><span class="line">      HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);</span><br><span class="line">      <span class="keyword">if</span> (request != <span class="keyword">null</span>) &#123; <span class="comment">//重定向数据的共享，RedirectView。先把数据移到request，再把request移到session</span></span><br><span class="line">         RequestContextUtils.getOutputFlashMap(request).putAll(flashAttributes);</span><br><span class="line">      &#125;</span><br><span class="line">   &#125;</span><br><span class="line">   <span class="keyword">return</span> mav;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<img src= "" data-lazy-src="https://upyunimg.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_10/image-20211020182823114.png"/>

<p>注意如果你返回值写index而不是index.jsp，它会给你报404。意思就是它不会给我们加jsp后缀。</p>
<h3 id="返回到DispatcherServlet-doDispatch"><a href="#返回到DispatcherServlet-doDispatch" class="headerlink" title="返回到DispatcherServlet#doDispatch()"></a>返回到DispatcherServlet#doDispatch()</h3><img src= "" data-lazy-src="https://upyunimg.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_10/image-20211020183211173.png">



<h3 id="DispatcherServlet-applyDefaultViewName"><a href="#DispatcherServlet-applyDefaultViewName" class="headerlink" title="DispatcherServlet#applyDefaultViewName()"></a>DispatcherServlet#applyDefaultViewName()</h3><figure class="highlight java"><table><tr><td class="code"><pre><span class="line">   <span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">applyDefaultViewName</span><span class="params">(HttpServletRequest request, <span class="meta">@Nullable</span> ModelAndView mv)</span> <span class="keyword">throws</span> Exception </span>&#123;</span><br><span class="line">      <span class="keyword">if</span> (mv != <span class="keyword">null</span> &amp;&amp; !mv.hasView()) &#123; <span class="comment">//如果没有指定跳转的页面</span></span><br><span class="line">         String defaultViewName = getDefaultViewName(request); <span class="comment">//给一个默认页面</span></span><br><span class="line">         <span class="keyword">if</span> (defaultViewName != <span class="keyword">null</span>) &#123;</span><br><span class="line">            mv.setViewName(defaultViewName);</span><br><span class="line">         &#125;</span><br><span class="line">      &#125;</span><br><span class="line">   &#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">protected</span> String <span class="title">getDefaultViewName</span><span class="params">(HttpServletRequest request)</span> <span class="keyword">throws</span> Exception </span>&#123;</span><br><span class="line">	<span class="keyword">return</span> (<span class="keyword">this</span>.viewNameTranslator != <span class="keyword">null</span> ? <span class="keyword">this</span>.viewNameTranslator.getViewName(request) : <span class="keyword">null</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="meta">@Nullable</span> <span class="comment">//把请求转成视图名（我们要跳转的页面地址）的翻译器【没啥用】</span></span><br><span class="line"><span class="keyword">private</span> RequestToViewNameTranslator viewNameTranslator;</span><br></pre></td></tr></table></figure>

<p>这里不是重点，我直接告诉你结果吧，默认页面就是把request的请求路径直接拿来当要去的页面地址 。</p>
<p>比如你的请求路径是<code>@GetMapping(&quot;/hello.html&quot;)</code>，但是你返回值写的是void，那么它就会给你返回到hello.html页面</p>
<h3 id="返回到DispatcherServlet-doDispatch-1"><a href="#返回到DispatcherServlet-doDispatch-1" class="headerlink" title="返回到DispatcherServlet#doDispatch()"></a>返回到DispatcherServlet#doDispatch()</h3><img src= "" data-lazy-src="https://upyunimg.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_10/image-20211020183829404.png" />

<p>来到拦截器的后置处理环节</p>
<img src= "" data-lazy-src="https://upyunimg.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_10/image-20211020183904018.png"  />

<p>然后来到处理结果环节</p>
<h3 id="springmvc能写哪些返回值"><a href="#springmvc能写哪些返回值" class="headerlink" title="springmvc能写哪些返回值"></a>springmvc能写哪些返回值</h3><p>官方文档：<a target="_blank" rel="noopener" href="https://docs.spring.io/spring-framework/docs/current/reference/html/web.html#mvc-ann-return-types">https://docs.spring.io/spring-framework/docs/current/reference/html/web.html#mvc-ann-return-types</a></p>
<h2 id="视图解析器解析流程"><a href="#视图解析器解析流程" class="headerlink" title="视图解析器解析流程"></a>视图解析器解析流程</h2><h3 id="DispatcherServlet-processDispatchResult-处理返回值"><a href="#DispatcherServlet-processDispatchResult-处理返回值" class="headerlink" title="DispatcherServlet#processDispatchResult()处理返回值"></a>DispatcherServlet#processDispatchResult()处理返回值</h3><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">processDispatchResult</span><span class="params">(HttpServletRequest request, HttpServletResponse response,</span></span></span><br><span class="line"><span class="function"><span class="params">      <span class="meta">@Nullable</span> HandlerExecutionChain mappedHandler, <span class="meta">@Nullable</span> ModelAndView mv,</span></span></span><br><span class="line"><span class="function"><span class="params">      <span class="meta">@Nullable</span> Exception exception)</span> <span class="keyword">throws</span> Exception </span>&#123;</span><br><span class="line"></span><br><span class="line">   <span class="keyword">boolean</span> errorView = <span class="keyword">false</span>;</span><br><span class="line"></span><br><span class="line">   <span class="keyword">if</span> (exception != <span class="keyword">null</span>) &#123; <span class="comment">//如果有异常处理异常</span></span><br><span class="line">      <span class="keyword">if</span> (exception <span class="keyword">instanceof</span> ModelAndViewDefiningException) &#123;</span><br><span class="line">         logger.debug(<span class="string">&quot;ModelAndViewDefiningException encountered&quot;</span>, exception);</span><br><span class="line">         mv = ((ModelAndViewDefiningException) exception).getModelAndView();<span class="comment">//即使有异常，这里也会返回ModelAndView</span></span><br><span class="line">      &#125;</span><br><span class="line">      <span class="keyword">else</span> &#123;  <span class="comment">//定义无数种异常解析器就会得到不同的异常解析效果</span></span><br><span class="line">         Object handler = (mappedHandler != <span class="keyword">null</span> ? mappedHandler.getHandler() : <span class="keyword">null</span>);</span><br><span class="line">         mv = processHandlerException(request, response, handler, exception);</span><br><span class="line">         errorView = (mv != <span class="keyword">null</span>);</span><br><span class="line">      &#125;</span><br><span class="line">   &#125;</span><br><span class="line"></span><br><span class="line">   <span class="comment">// 动态策略。 Did the handler return a view to render?   为啥？@ResponseBody（提前在解析返回值的时候，就已经把数据写出去了，所以这一步就没有了）</span></span><br><span class="line">   <span class="keyword">if</span> (mv != <span class="keyword">null</span> &amp;&amp; !mv.wasCleared()) &#123;</span><br><span class="line">      render(mv, request, response); <span class="comment">//渲染ModeAndView，来解析模型和视图；最终决定响应效果</span></span><br><span class="line">      <span class="keyword">if</span> (errorView) &#123;</span><br><span class="line">         WebUtils.clearErrorRequestAttributes(request);</span><br><span class="line">      &#125;</span><br><span class="line">   &#125;</span><br><span class="line">   <span class="keyword">else</span> &#123;</span><br><span class="line">      <span class="keyword">if</span> (logger.isTraceEnabled()) &#123;</span><br><span class="line">         logger.trace(<span class="string">&quot;No view rendering, null ModelAndView returned.&quot;</span>);</span><br><span class="line">      &#125;</span><br><span class="line">   &#125;</span><br><span class="line"></span><br><span class="line">   <span class="keyword">if</span> (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) &#123;</span><br><span class="line">      <span class="comment">// Concurrent handling started during a forward</span></span><br><span class="line">      <span class="keyword">return</span>;</span><br><span class="line">   &#125;</span><br><span class="line"></span><br><span class="line">   <span class="keyword">if</span> (mappedHandler != <span class="keyword">null</span>) &#123;</span><br><span class="line">      <span class="comment">// Exception (if any) is already handled..</span></span><br><span class="line">      mappedHandler.triggerAfterCompletion(request, response, <span class="keyword">null</span>);</span><br><span class="line">   &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>进入此方法</p>
<img src= "" data-lazy-src="https://upyunimg.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_10/image-20211020184308914.png"/>



<h3 id="DispatcherServlet-render-渲染ModelAndView"><a href="#DispatcherServlet-render-渲染ModelAndView" class="headerlink" title="DispatcherServlet#render()渲染ModelAndView"></a>DispatcherServlet#render()渲染ModelAndView</h3><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">protected</span> <span class="keyword">void</span> <span class="title">render</span><span class="params">(ModelAndView mv, HttpServletRequest request, HttpServletResponse response)</span> <span class="keyword">throws</span> Exception </span>&#123;</span><br><span class="line">   <span class="comment">// Determine locale for request and apply it to the response.</span></span><br><span class="line">   Locale locale =</span><br><span class="line">         (<span class="keyword">this</span>.localeResolver != <span class="keyword">null</span> ? <span class="keyword">this</span>.localeResolver.resolveLocale(request) : request.getLocale());</span><br><span class="line">   response.setLocale(locale);</span><br><span class="line"></span><br><span class="line">   View view;</span><br><span class="line">   String viewName = mv.getViewName();</span><br><span class="line">   <span class="keyword">if</span> (viewName != <span class="keyword">null</span>) &#123;</span><br><span class="line">      <span class="comment">// We need to resolve the view name.  关键还是这里</span></span><br><span class="line">      view = resolveViewName(viewName, mv.getModelInternal(), locale, request);</span><br><span class="line">      <span class="keyword">if</span> (view == <span class="keyword">null</span>) &#123;</span><br><span class="line">         <span class="keyword">throw</span> <span class="keyword">new</span> ServletException(<span class="string">&quot;Could not resolve view with name &#x27;&quot;</span> + mv.getViewName() +</span><br><span class="line">               <span class="string">&quot;&#x27; in servlet with name &#x27;&quot;</span> + getServletName() + <span class="string">&quot;&#x27;&quot;</span>);</span><br><span class="line">      &#125;</span><br><span class="line">   &#125;</span><br><span class="line">   <span class="keyword">else</span> &#123;</span><br><span class="line">      <span class="comment">// No need to lookup: the ModelAndView object contains the actual View object.</span></span><br><span class="line">      view = mv.getView();</span><br><span class="line">      <span class="keyword">if</span> (view == <span class="keyword">null</span>) &#123;</span><br><span class="line">         <span class="keyword">throw</span> <span class="keyword">new</span> ServletException(<span class="string">&quot;ModelAndView [&quot;</span> + mv + <span class="string">&quot;] neither contains a view name nor a &quot;</span> +</span><br><span class="line">               <span class="string">&quot;View object in servlet with name &#x27;&quot;</span> + getServletName() + <span class="string">&quot;&#x27;&quot;</span>);</span><br><span class="line">      &#125;</span><br><span class="line">   &#125;</span><br><span class="line"></span><br><span class="line">   <span class="comment">// Delegate to the View object for rendering.</span></span><br><span class="line">   <span class="keyword">if</span> (logger.isTraceEnabled()) &#123;</span><br><span class="line">      logger.trace(<span class="string">&quot;Rendering view [&quot;</span> + view + <span class="string">&quot;] &quot;</span>);</span><br><span class="line">   &#125;</span><br><span class="line">   <span class="keyword">try</span> &#123;</span><br><span class="line">      <span class="keyword">if</span> (mv.getStatus() != <span class="keyword">null</span>) &#123;</span><br><span class="line">         response.setStatus(mv.getStatus().value());</span><br><span class="line">      &#125;</span><br><span class="line">      view.render(mv.getModelInternal(), request, response);</span><br><span class="line">   &#125;</span><br><span class="line">   <span class="keyword">catch</span> (Exception ex) &#123;</span><br><span class="line">      <span class="keyword">if</span> (logger.isDebugEnabled()) &#123;</span><br><span class="line">         logger.debug(<span class="string">&quot;Error rendering view [&quot;</span> + view + <span class="string">&quot;]&quot;</span>, ex);</span><br><span class="line">      &#125;</span><br><span class="line">      <span class="keyword">throw</span> ex;</span><br><span class="line">   &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<h3 id="DispatcherServlet-resolveViewName"><a href="#DispatcherServlet-resolveViewName" class="headerlink" title="DispatcherServlet#resolveViewName()"></a>DispatcherServlet#resolveViewName()</h3><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">protected</span> View <span class="title">resolveViewName</span><span class="params">(String viewName, <span class="meta">@Nullable</span> Map&lt;String, Object&gt; model,</span></span></span><br><span class="line"><span class="function"><span class="params">      Locale locale, HttpServletRequest request)</span> <span class="keyword">throws</span> Exception </span>&#123;</span><br><span class="line"></span><br><span class="line">   <span class="keyword">if</span> (<span class="keyword">this</span>.viewResolvers != <span class="keyword">null</span>) &#123;</span><br><span class="line">      <span class="comment">//一样的for循环视图解析器</span></span><br><span class="line">      <span class="keyword">for</span> (ViewResolver viewResolver : <span class="keyword">this</span>.viewResolvers) &#123;</span><br><span class="line">         View view = viewResolver.resolveViewName(viewName, locale);</span><br><span class="line">         <span class="keyword">if</span> (view != <span class="keyword">null</span>) &#123;</span><br><span class="line">            <span class="keyword">return</span> view;</span><br><span class="line">         &#125;</span><br><span class="line">      &#125;</span><br><span class="line">   &#125;</span><br><span class="line">   <span class="keyword">return</span> <span class="keyword">null</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>不过默认的视图解析器只有一个</p>
<img src= "" data-lazy-src="https://upyunimg.imlql.cn/youthlql@1.0.6/spring-sourcecode-v1/chapter_10/image-20211020184754506.png"/>

<p>这里没有拼接前缀和后缀，所以我们必须要自己写.jsp或者.html</p>
<h3 id="RequestResponseBodyMethodProcessor即是返回值解析器也是参数解析器"><a href="#RequestResponseBodyMethodProcessor即是返回值解析器也是参数解析器" class="headerlink" title="RequestResponseBodyMethodProcessor即是返回值解析器也是参数解析器"></a>RequestResponseBodyMethodProcessor即是返回值解析器也是参数解析器</h3><figure class="highlight java"><table><tr><td class="code"><pre><span class="line">   <span class="meta">@Override</span></span><br><span class="line">   <span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">supportsParameter</span><span class="params">(MethodParameter parameter)</span> </span>&#123;</span><br><span class="line">      <span class="keyword">return</span> parameter.hasParameterAnnotation(RequestBody.class);</span><br><span class="line">   &#125;</span><br><span class="line"></span><br><span class="line">   <span class="meta">@Override</span></span><br><span class="line">   <span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">supportsReturnType</span><span class="params">(MethodParameter returnType)</span> </span>&#123;</span><br><span class="line">      <span class="keyword">return</span> (AnnotatedElementUtils.hasAnnotation(returnType.getContainingClass(), ResponseBody.class) ||</span><br><span class="line">            returnType.hasMethodAnnotation(ResponseBody.class));</span><br><span class="line">   &#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">public</span> Object <span class="title">resolveArgument</span><span class="params">(MethodParameter parameter, <span class="meta">@Nullable</span> ModelAndViewContainer mavContainer,</span></span></span><br><span class="line"><span class="function"><span class="params">		NativeWebRequest webRequest, <span class="meta">@Nullable</span> WebDataBinderFactory binderFactory)</span> <span class="keyword">throws</span> Exception </span>&#123;</span><br><span class="line"></span><br><span class="line">	parameter = parameter.nestedIfOptional();</span><br><span class="line">	Object arg = readWithMessageConverters(webRequest, parameter, parameter.getNestedGenericParameterType());</span><br><span class="line">	String name = Conventions.getVariableNameForParameter(parameter);</span><br><span class="line"></span><br><span class="line">	<span class="keyword">if</span> (binderFactory != <span class="keyword">null</span>) &#123;</span><br><span class="line">		WebDataBinder binder = binderFactory.createBinder(webRequest, arg, name);</span><br><span class="line">		<span class="keyword">if</span> (arg != <span class="keyword">null</span>) &#123;</span><br><span class="line">			validateIfApplicable(binder, parameter);</span><br><span class="line">			<span class="keyword">if</span> (binder.getBindingResult().hasErrors() &amp;&amp; isBindExceptionRequired(binder, parameter)) &#123;</span><br><span class="line">				<span class="keyword">throw</span> <span class="keyword">new</span> MethodArgumentNotValidException(parameter, binder.getBindingResult());</span><br><span class="line">			&#125;</span><br><span class="line">		&#125;</span><br><span class="line">		<span class="keyword">if</span> (mavContainer != <span class="keyword">null</span>) &#123;</span><br><span class="line">			mavContainer.addAttribute(BindingResult.MODEL_KEY_PREFIX + name, binder.getBindingResult());</span><br><span class="line">		&#125;</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	<span class="keyword">return</span> adaptArgumentIfNecessary(arg, parameter);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="meta">@Override</span>  <span class="comment">//如果返回值标注了 @ResponseBody注解。会被这个人拦截处理器</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">handleReturnValue</span><span class="params">(<span class="meta">@Nullable</span> Object returnValue, MethodParameter returnType,</span></span></span><br><span class="line"><span class="function"><span class="params">		ModelAndViewContainer mavContainer, NativeWebRequest webRequest)</span></span></span><br><span class="line"><span class="function">		<span class="keyword">throws</span> IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException </span>&#123;</span><br><span class="line"></span><br><span class="line">	mavContainer.setRequestHandled(<span class="keyword">true</span>);</span><br><span class="line">	ServletServerHttpRequest inputMessage = createInputMessage(webRequest);</span><br><span class="line">	ServletServerHttpResponse outputMessage = createOutputMessage(webRequest);</span><br><span class="line"></span><br><span class="line">	<span class="comment">// Try even with null return value. ResponseBodyAdvice could get involved.</span></span><br><span class="line">	writeWithMessageConverters(returnValue, returnType, inputMessage, outputMessage);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<p>由于现在的前后端分离时代，我们直接返回视图的需求已经很少很少了。现在一般都是直接返回数据，所以视图解析器详细原理不再细述，后面就直接开始讲异常处理流程。</p>
</article><div class="post-copyright"><div class="post-copyright__author"><span class="post-copyright-meta">文章作者: </span><span class="post-copyright-info"><a href="mailto:undefined">youthlql</a></span></div><div class="post-copyright__type"><span class="post-copyright-meta">文章链接: </span><span class="post-copyright-info"><a href="https://imlql.cn/post/453f802e.html">https://imlql.cn/post/453f802e.html</a></span></div><div class="post-copyright__notice"><span class="post-copyright-meta">版权声明: </span><span class="post-copyright-info">本博客所有文章除特别声明外，均采用 <a href="https://creativecommons.org/licenses/by-nc-sa/4.0/" target="_blank">CC BY-NC-SA 4.0</a> 许可协议。转载请注明来自 <a href="https://imlql.cn" target="_blank">风祈的时光录</a>！</span></div></div><div class="tag_share"><div class="post-meta__tag-list"><a class="post-meta__tags" href="/tags/Spring%E6%BA%90%E7%A0%81/">Spring源码</a></div><div class="post_share"></div></div><div class="post-reward"><div class="reward-button"><i class="fas fa-qrcode"></i> 打赏</div><div class="reward-main"><ul class="reward-all"><li class="reward-item"><a href="https://npm.elemecdn.com/youthlql@1.0.8/blog/wechat.jpg" target="_blank"><img class="post-qr-code-img" src= "" data-lazy-src="https://npm.elemecdn.com/youthlql@1.0.8/blog/wechat.jpg" alt="微信"/></a><div class="post-qr-code-desc">微信</div></li><li class="reward-item"><a href="https://npm.elemecdn.com/youthlql@1.0.8/blog/alipay.jpg" target="_blank"><img class="post-qr-code-img" src= "" data-lazy-src="https://npm.elemecdn.com/youthlql@1.0.8/blog/alipay.jpg" alt="支付宝"/></a><div class="post-qr-code-desc">支付宝</div></li></ul></div></div><nav class="pagination-post" id="pagination"><div class="prev-post pull-left"><a href="/post/4dfb096b.html"><img class="prev-cover" src= "" data-lazy-src="https://npm.elemecdn.com/lql_static@latest/logo/spring.png" onerror="onerror=null;src='/img/404.jpg'" alt="cover of previous post"><div class="pagination-info"><div class="label">上一篇</div><div class="prev_info">Spring源码系列-第11章-SpringMVC异常处理源码和@EnableWebMvc原理</div></div></a></div><div class="next-post pull-right"><a href="/post/6f2cef28.html"><img class="next-cover" src= "" data-lazy-src="https://npm.elemecdn.com/lql_static@latest/logo/spring.png" onerror="onerror=null;src='/img/404.jpg'" alt="cover of next post"><div class="pagination-info"><div class="label">下一篇</div><div class="next_info">Spring源码系列-第9章-SpringMVC请求处理源码和HandlerMapping原理</div></div></a></div></nav><div class="relatedPosts"><div class="headline"><i class="fas fa-thumbs-up fa-fw"></i><span>相关推荐</span></div><div class="relatedPosts-list"><div><a href="/post/41fb8d9e.html" title="Spring源码系列-第2章-后置工厂处理器和Bean生命周期"><img class="cover" src= "" data-lazy-src="https://npm.elemecdn.com/lql_static@latest/logo/spring.png" alt="cover"><div class="content is-center"><div class="date"><i class="far fa-calendar-alt fa-fw"></i> 2021-12-21</div><div class="title">Spring源码系列-第2章-后置工厂处理器和Bean生命周期</div></div></a></div><div><a href="/post/49f419ae.html" title="Spring源码系列-第4章-Bean初始化流程"><img class="cover" src= "" data-lazy-src="https://npm.elemecdn.com/lql_static@latest/logo/spring.png" alt="cover"><div class="content is-center"><div class="date"><i class="far fa-calendar-alt fa-fw"></i> 2022-01-27</div><div class="title">Spring源码系列-第4章-Bean初始化流程</div></div></a></div><div><a href="/post/fb3552e0.html" title="Spring源码系列-第7章-AOP的执行流程原理和监听器原理"><img class="cover" src= "" data-lazy-src="https://npm.elemecdn.com/lql_static@latest/logo/spring.png" alt="cover"><div class="content is-center"><div class="date"><i class="far fa-calendar-alt fa-fw"></i> 2022-04-17</div><div class="title">Spring源码系列-第7章-AOP的执行流程原理和监听器原理</div></div></a></div><div><a href="/post/c8dd1418.html" title="Spring源码系列-第8章-SpringMVC子容器和Spring父容器的启动原理"><img class="cover" src= "" data-lazy-src="https://npm.elemecdn.com/lql_static@latest/logo/spring.png" alt="cover"><div class="content is-center"><div class="date"><i class="far fa-calendar-alt fa-fw"></i> 2022-06-20</div><div class="title">Spring源码系列-第8章-SpringMVC子容器和Spring父容器的启动原理</div></div></a></div><div><a href="/post/6f2cef28.html" title="Spring源码系列-第9章-SpringMVC请求处理源码和HandlerMapping原理"><img class="cover" src= "" data-lazy-src="https://npm.elemecdn.com/lql_static@latest/logo/spring.png" alt="cover"><div class="content is-center"><div class="date"><i class="far fa-calendar-alt fa-fw"></i> 2022-06-21</div><div class="title">Spring源码系列-第9章-SpringMVC请求处理源码和HandlerMapping原理</div></div></a></div><div><a href="/post/4dfb096b.html" title="Spring源码系列-第11章-SpringMVC异常处理源码和@EnableWebMvc原理"><img class="cover" src= "" data-lazy-src="https://npm.elemecdn.com/lql_static@latest/logo/spring.png" alt="cover"><div class="content is-center"><div class="date"><i class="far fa-calendar-alt fa-fw"></i> 2023-01-05</div><div class="title">Spring源码系列-第11章-SpringMVC异常处理源码和@EnableWebMvc原理</div></div></a></div></div></div></div><div class="aside-content" id="aside-content"><div class="sticky_layout"><div class="card-widget" id="card-toc"><div class="item-headline"><i class="fas fa-stream"></i><span>目录</span><span class="toc-percentage"></span></div><div class="toc-content is-expand"><ol class="toc"><li class="toc-item toc-level-1"><a class="toc-link" href="#%E7%AC%AC10%E7%AB%A0-SpringMVC%E4%B8%AD%E7%9A%84HandlerAdapter%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90"><span class="toc-text">第10章-SpringMVC中的HandlerAdapter源码解析</span></a><ol class="toc-child"><li class="toc-item toc-level-2"><a class="toc-link" href="#%E6%B5%8B%E8%AF%95%E7%B1%BB"><span class="toc-text">测试类</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#HelloController"><span class="toc-text">HelloController</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#index-jsp"><span class="toc-text">index.jsp</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#BeanNameUrlHandlerMapping%E7%AE%80%E4%BB%8B"><span class="toc-text">BeanNameUrlHandlerMapping简介</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#HandlerAdapter%E6%A6%82%E8%BF%B0"><span class="toc-text">HandlerAdapter概述</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#DispatcherServlet-doDispatch-%E8%AF%B7%E6%B1%82%E6%B4%BE%E5%8F%91"><span class="toc-text">DispatcherServlet#doDispatch()请求派发</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#DispatcherServlet-getHandlerAdapter"><span class="toc-text">DispatcherServlet#getHandlerAdapter()</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#HttpRequestHandlerAdapter-supports"><span class="toc-text">HttpRequestHandlerAdapter#supports()</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#SimpleControllerHandlerAdapter-supports"><span class="toc-text">SimpleControllerHandlerAdapter#supports()</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#RequestMappingHandlerAdapter"><span class="toc-text">RequestMappingHandlerAdapter</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#AbstractHandlerMethodAdapter-supports"><span class="toc-text">AbstractHandlerMethodAdapter#supports()</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#RequestMappingHandlerAdapter-supportsInternal"><span class="toc-text">RequestMappingHandlerAdapter#supportsInternal()</span></a></li></ol></li><li class="toc-item toc-level-3"><a class="toc-link" href="#DispatcherServlet-doDispatch"><span class="toc-text">DispatcherServlet#doDispatch()</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E4%B8%BE%E4%BE%8B%E8%AF%B4%E6%98%8EHttpRequestHandlerAdapter%E5%92%8CSimpleControllerHandlerAdapter%E5%A6%82%E4%BD%95%E4%B8%8EBeanNameUrlHandlerMapping%E8%81%94%E5%8A%A8"><span class="toc-text">举例说明HttpRequestHandlerAdapter和SimpleControllerHandlerAdapter如何与BeanNameUrlHandlerMapping联动</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E6%B5%8B%E8%AF%95%E7%B1%BB-HelloHttpRequestHandler"><span class="toc-text">测试类-HelloHttpRequestHandler</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#DispatcherServlet-getHandlerAdapter-1"><span class="toc-text">DispatcherServlet#getHandlerAdapter()</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#DispatcherServlet-doDispatch-1"><span class="toc-text">DispatcherServlet#doDispatch()</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#HttpRequestHandlerAdapter-handle"><span class="toc-text">HttpRequestHandlerAdapter#handle()</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E6%B5%8B%E8%AF%95%E7%B1%BB-HelloSimpleController"><span class="toc-text">测试类-HelloSimpleController</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%B0%8F%E6%80%BB%E7%BB%93"><span class="toc-text">小总结</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#RequestMappingHandlerAdapter%E4%B8%AD%E7%9A%84%E5%8F%82%E6%95%B0%E8%A7%A3%E6%9E%90%E5%99%A8%E3%80%81%E8%BF%94%E5%9B%9E%E5%80%BC%E5%A4%84%E7%90%86%E5%99%A8-gt-%E6%A6%82%E8%BF%B0"><span class="toc-text">RequestMappingHandlerAdapter中的参数解析器、返回值处理器&#x3D;&gt;概述</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#DispatcherServlet-doDispatch-2"><span class="toc-text">DispatcherServlet#doDispatch()</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#AbstractHandlerMethodAdapter-handle"><span class="toc-text">AbstractHandlerMethodAdapter#handle()</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#RequestMappingHandlerAdapter-handleInternal"><span class="toc-text">RequestMappingHandlerAdapter#handleInternal()</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#RequestMappingHandlerAdapter-invokeHandlerMethod"><span class="toc-text">RequestMappingHandlerAdapter#invokeHandlerMethod( )</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#%E4%BB%80%E4%B9%88%E6%98%AFWebDataBinderFactory%EF%BC%9F"><span class="toc-text">什么是WebDataBinderFactory？</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#argumentResolvers%E5%8F%82%E6%95%B0%E8%A7%A3%E6%9E%90%E5%99%A8"><span class="toc-text">argumentResolvers参数解析器</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#returnValueHandlers%E8%BF%94%E5%9B%9E%E5%80%BC%E8%A7%A3%E6%9E%90%E5%99%A8"><span class="toc-text">returnValueHandlers返回值解析器</span></a></li></ol></li><li class="toc-item toc-level-3"><a class="toc-link" href="#15%E4%B8%AA%E5%8F%82%E6%95%B0%E8%A7%A3%E6%9E%90%E5%99%A8%E5%92%8C27%E4%B8%AA%E8%BF%94%E5%9B%9E%E5%80%BC%E8%A7%A3%E6%9E%90%E5%99%A8%E6%98%AF%E4%BB%80%E4%B9%88%E6%97%B6%E5%80%99%E6%9C%89%E5%80%BC%E7%9A%84%EF%BC%9F"><span class="toc-text">15个参数解析器和27个返回值解析器是什么时候有值的？</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#RequestMappingHandlerAdapter-afterPropertiesSet"><span class="toc-text">RequestMappingHandlerAdapter#afterPropertiesSet()</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#RequestMappingHandlerAdapter-getDefaultArgumentResolvers"><span class="toc-text">RequestMappingHandlerAdapter#getDefaultArgumentResolvers()</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#RequestMappingHandlerAdapter-getDefaultReturnValueHandlers"><span class="toc-text">RequestMappingHandlerAdapter#getDefaultReturnValueHandlers()</span></a></li></ol></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#RequestMappingHandlerAdapter%E4%B8%AD%E7%9A%84%E5%8F%82%E6%95%B0%E8%A7%A3%E6%9E%90%E5%99%A8%E5%B7%A5%E4%BD%9C%E6%B5%81%E7%A8%8B"><span class="toc-text">RequestMappingHandlerAdapter中的参数解析器工作流程</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#DispatcherServlet-doDispatch-3"><span class="toc-text">DispatcherServlet#doDispatch()</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#RequestMappingHandlerAdapter-invokeHandlerMethod-%E5%87%86%E5%A4%87%E6%89%A7%E8%A1%8C%E7%9B%AE%E6%A0%87%E6%96%B9%E6%B3%95"><span class="toc-text">RequestMappingHandlerAdapter#invokeHandlerMethod()准备执行目标方法</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#ServletInvocableHandlerMethod-invokeAndHandle-%E7%9C%9F%E6%AD%A3%E5%BC%80%E5%A7%8B%E6%89%A7%E8%A1%8C%E7%9B%AE%E6%A0%87%E6%96%B9%E6%B3%95"><span class="toc-text">ServletInvocableHandlerMethod#invokeAndHandle()真正开始执行目标方法</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#InvocableHandlerMethod-invokeForRequest-%E7%9B%AE%E6%A0%87%E6%96%B9%E6%B3%95%E7%9A%84%E5%8F%8D%E5%B0%84%E6%89%A7%E8%A1%8C%E8%BF%87%E7%A8%8B"><span class="toc-text">InvocableHandlerMethod#invokeForRequest( )目标方法的反射执行过程</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#InvocableHandlerMethod-getMethodArgumentValues-%E8%8E%B7%E5%8F%96%E6%96%B9%E6%B3%95%E7%9A%84%E8%AF%B7%E6%B1%82%E5%8F%82%E6%95%B0"><span class="toc-text">InvocableHandlerMethod#getMethodArgumentValues()获取方法的请求参数</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#HandlerMethodArgumentResolverComposite%E5%87%86%E5%A4%87%E5%BE%AA%E7%8E%AF27%E4%B8%AA%E5%8F%82%E6%95%B0%E8%A7%A3%E6%9E%90%E5%99%A8"><span class="toc-text">HandlerMethodArgumentResolverComposite准备循环27个参数解析器</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#HandlerMethodArgumentResolverComposite-supportsParameter"><span class="toc-text">HandlerMethodArgumentResolverComposite#supportsParameter()</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#HandlerMethodArgumentResolverComposite-getArgumentResolver-%E5%BE%AA%E7%8E%AF%E5%88%A4%E6%96%AD%E5%93%AA%E4%B8%AA%E5%8F%82%E6%95%B0%E8%A7%A3%E6%9E%90%E5%99%A8%E6%94%AF%E6%8C%81%E8%BF%99%E4%B8%AA%E5%8F%82%E6%95%B0"><span class="toc-text">HandlerMethodArgumentResolverComposite#getArgumentResolver()循环判断哪个参数解析器支持这个参数</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#RequestParamMethodArgumentResolver%E5%88%A4%E6%96%AD-RequestParam%E6%B3%A8%E8%A7%A3%E7%9A%84%E5%8F%82%E6%95%B0%E8%A7%A3%E6%9E%90%E5%99%A8"><span class="toc-text">RequestParamMethodArgumentResolver判断@RequestParam注解的参数解析器</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#RequestParamMapMethodArgumentResolver%E5%8F%82%E6%95%B0%E8%A7%A3%E6%9E%90%E5%99%A8"><span class="toc-text">RequestParamMapMethodArgumentResolver参数解析器</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#PathVariableMethodArgumentResolver%E5%88%A4%E6%96%AD-PathVariable%E6%B3%A8%E8%A7%A3%E7%9A%84%E5%8F%82%E6%95%B0%E8%A7%A3%E6%9E%90%E5%99%A8"><span class="toc-text">PathVariableMethodArgumentResolver判断@PathVariable注解的参数解析器</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E8%BF%94%E5%9B%9E%E5%88%B0InvocableHandlerMethod-getMethodArgumentValues"><span class="toc-text">返回到InvocableHandlerMethod#getMethodArgumentValues( )</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E8%BF%94%E5%9B%9E%E5%88%B0InvocableHandlerMethod-invokeForRequest"><span class="toc-text">返回到InvocableHandlerMethod#invokeForRequest()</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E8%BF%94%E5%9B%9E%E5%88%B0ServletInvocableHandlerMethod-invokeAndHandle"><span class="toc-text">返回到ServletInvocableHandlerMethod#invokeAndHandle()</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#SpringMVC%E5%88%B0%E5%BA%95%E8%83%BD%E5%86%99%E5%93%AA%E4%BA%9B%E5%8F%82%E6%95%B0%EF%BC%9F"><span class="toc-text">SpringMVC到底能写哪些参数？</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#RequestMappingHandlerAdapter%E4%B8%AD%E7%9A%84%E8%BF%94%E5%9B%9E%E5%80%BC%E8%A7%A3%E6%9E%90%E5%99%A8%E5%B7%A5%E4%BD%9C%E6%B5%81%E7%A8%8B"><span class="toc-text">RequestMappingHandlerAdapter中的返回值解析器工作流程</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#ServletInvocableHandlerMethod-invokeAndHandle"><span class="toc-text">ServletInvocableHandlerMethod#invokeAndHandle()</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#HandlerMethodReturnValueHandlerComposite-handleReturnValue"><span class="toc-text">HandlerMethodReturnValueHandlerComposite#handleReturnValue()</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#HandlerMethodReturnValueHandlerComposite-selectHandler-%E5%BE%AA%E7%8E%AF15%E4%B8%AA%E8%BF%94%E5%9B%9E%E5%80%BC%E5%A4%84%E7%90%86%E5%99%A8%E6%89%BE%E5%88%B0%E5%90%88%E9%80%82%E7%9A%84%E8%BF%94%E5%9B%9E%E5%80%BC%E5%A4%84%E7%90%86%E5%99%A8"><span class="toc-text">HandlerMethodReturnValueHandlerComposite#selectHandler()循环15个返回值处理器找到合适的返回值处理器</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#ViewNameMethodReturnValueHandler-supportsReturnType"><span class="toc-text">ViewNameMethodReturnValueHandler#supportsReturnType()</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E8%BF%94%E5%9B%9E%E5%88%B0HandlerMethodReturnValueHandlerComposite-handleReturnValue"><span class="toc-text">返回到HandlerMethodReturnValueHandlerComposite#handleReturnValue()</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#ViewNameMethodReturnValueHandler-handleReturnValue-%E5%BC%80%E5%A7%8B%E5%A4%84%E7%90%86%E8%BF%94%E5%9B%9E%E5%80%BC"><span class="toc-text">ViewNameMethodReturnValueHandler#handleReturnValue()开始处理返回值</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E8%BF%94%E5%9B%9E%E5%88%B0RequestMappingHandlerAdapter-invokeHandlerMethod"><span class="toc-text">返回到RequestMappingHandlerAdapter#invokeHandlerMethod()</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#RequestMappingHandlerAdapter-getModelAndView-%E8%BF%9B%E8%A1%8C%E8%A7%86%E5%9B%BE%E8%A7%A3%E6%9E%90%E7%9B%B8%E5%85%B3%E5%B7%A5%E4%BD%9C"><span class="toc-text">RequestMappingHandlerAdapter#getModelAndView()进行视图解析相关工作</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E8%BF%94%E5%9B%9E%E5%88%B0DispatcherServlet-doDispatch"><span class="toc-text">返回到DispatcherServlet#doDispatch()</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#DispatcherServlet-applyDefaultViewName"><span class="toc-text">DispatcherServlet#applyDefaultViewName()</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E8%BF%94%E5%9B%9E%E5%88%B0DispatcherServlet-doDispatch-1"><span class="toc-text">返回到DispatcherServlet#doDispatch()</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#springmvc%E8%83%BD%E5%86%99%E5%93%AA%E4%BA%9B%E8%BF%94%E5%9B%9E%E5%80%BC"><span class="toc-text">springmvc能写哪些返回值</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E8%A7%86%E5%9B%BE%E8%A7%A3%E6%9E%90%E5%99%A8%E8%A7%A3%E6%9E%90%E6%B5%81%E7%A8%8B"><span class="toc-text">视图解析器解析流程</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#DispatcherServlet-processDispatchResult-%E5%A4%84%E7%90%86%E8%BF%94%E5%9B%9E%E5%80%BC"><span class="toc-text">DispatcherServlet#processDispatchResult()处理返回值</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#DispatcherServlet-render-%E6%B8%B2%E6%9F%93ModelAndView"><span class="toc-text">DispatcherServlet#render()渲染ModelAndView</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#DispatcherServlet-resolveViewName"><span class="toc-text">DispatcherServlet#resolveViewName()</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#RequestResponseBodyMethodProcessor%E5%8D%B3%E6%98%AF%E8%BF%94%E5%9B%9E%E5%80%BC%E8%A7%A3%E6%9E%90%E5%99%A8%E4%B9%9F%E6%98%AF%E5%8F%82%E6%95%B0%E8%A7%A3%E6%9E%90%E5%99%A8"><span class="toc-text">RequestResponseBodyMethodProcessor即是返回值解析器也是参数解析器</span></a></li></ol></li></ol></li></ol></div></div></div></div></main><footer id="footer"><div id="footer-wrap"><div class="copyright">&copy;2020 - 2023 By youthlql</div><div class="framework-info"><span>框架 </span><a target="_blank" rel="noopener" href="https://hexo.io">Hexo</a><span class="footer-separator">|</span><span>主题 </span><a target="_blank" rel="noopener" href="https://github.com/jerryc127/hexo-theme-butterfly">Butterfly</a></div><div class="footer_custom_text"><a href="https://www.upyun.com/?utm_source=lianmeng&utm_medium=referral" target="_blank" rel="noopener" class="one-pan-link-mark"><img style="position:relative;top:-3px; " src= "" data-lazy-src="https://npm.elemecdn.com/lql_static@latest/upyun/logo.png" align="absmiddle" width="60px" height="30px"></a><a target="_blank" rel="noopener" href="https://beian.miit.gov.cn"><img class="icp-icon" src= "" data-lazy-src="https://npm.elemecdn.com/lql_static@latest/logo/icp.png"><span>鄂ICP备19028890号</span></a></div></div></footer></div><div id="rightside"><div id="rightside-config-hide"><button id="readmode" type="button" title="阅读模式"><i class="fas fa-book-open"></i></button><button id="translateLink" type="button" title="简繁转换">简</button><button id="darkmode" type="button" title="浅色和深色模式转换"><i class="fas fa-adjust"></i></button><button id="hide-aside-btn" type="button" title="单栏和双栏切换"><i class="fas fa-arrows-alt-h"></i></button></div><div id="rightside-config-show"><button id="rightside_config" type="button" title="设置"><i class="fas fa-cog fa-spin"></i></button><button class="close" id="mobile-toc-button" type="button" title="目录"><i class="fas fa-list-ul"></i></button><button id="go-up" type="button" title="回到顶部"><i class="fas fa-arrow-up"></i></button></div></div><div id="local-search"><div class="search-dialog"><nav class="search-nav"><span class="search-dialog-title">本地搜索</span><span id="loading-status"></span><button class="search-close-button"><i class="fas fa-times"></i></button></nav><div class="is-center" id="loading-database"><i class="fas fa-spinner fa-pulse"></i><span>  数据库加载中</span></div><div class="search-wrap"><div id="local-search-input"><div class="local-search-box"><input class="local-search-box--input" placeholder="搜索文章" type="text"/></div></div><hr/><div id="local-search-results"></div></div></div><div id="search-mask"></div></div><div><script src="/js/utils.js"></script><script src="/js/main.js"></script><script src="/js/tw_cn.js"></script><script src="https://npm.elemecdn.com/@fancyapps/ui/dist/fancybox.umd.js"></script><script src="https://npm.elemecdn.com/instant.page/instantpage.js" type="module"></script><script src="https://cdn.jsdelivr.net/npm/vanilla-lazyload/dist/lazyload.iife.min.js"></script><script src="/js/search/local-search.js"></script><div class="js-pjax"></div><script defer src="https://npm.elemecdn.com/jquery@latest/dist/jquery.min.js"></script></div></body></html>